Java程序设计语言提供了三种可抛出结构:受检的异常(checked exception)、运行时异常(run-time exception)和错误(error)。关于什么时候适合使用哪种可抛出结构,程序员中间存在一些困惑。虽然这项决定并不总是那么清晰,但还是有些一般性的原则提出了强有力的指导。
在决定使用受检的异常或是未受检的异常时,主要的原则是:如果期望调用者能够适当的恢复,对于这种情况就应该使用受检的异常。通常抛出受检的异常,强迫调用者在一个catch子句中处理该异常,或者将他传播出去。因此,方法中声明要抛出的每个受检的异常,都是对API用户的一种潜在指示:与异常相关联的条件是调用这个方法的一种可能的结果。
API的设计者让API用户面对受检的异常,以此强制用户从这个异常条件中恢复。用户可以忽视这样的强制要求,只需捕获异常并忽略即可,但这往往不是个好办法。
有两种未受检的可抛出结构:运行时异常和错误。在行为上两者都是等同的:他们都是不许哟啊也不应该被捕获的可抛出结构。如果程序抛出未受检的异常或者错误,往往就属于不可恢复的情形,继续执行下去有害无益。如果程序没有捕捉到这样的可抛出结构,将会导致当前线程停止,并出现适当的错误信息。
用运行时异常来表明编程错误。大多数的运行时异常都表示前提违例。所谓前提违例是指API的客户没有遵守API规范建立的约定。例如,数组访问的约定指明了数组的下标值必须在零和数组长度减1之间。ArrayIndexOutOfBoundsException表明这个前提被违反了。
虽然JLS(Java语言规范)并没有要求,但是按照惯例,错误往往被JVM保留用于表示资源不足、约束失败,或者其他使程序无法继续执行的条件。由于这已经是个几乎被普遍接受的惯例,因此最好不要再实现任何新的Error子类。因此,你实现的所有未受检的抛出结构都应该是RuntimeException的子类(直接的或者是间接的)。
要想定义一个抛出结构,他不是Exception、RuntimeException或Error的子类,这也是可能的。JLS并没有直接规定这样的抛出结构,而是隐式的制定了:从行为意义上将他们等同于普通的受检异常(即Exception的子类,但不是RuntimException的子类)。那么,什么时候应该使用这样的抛出结构呢?总之,永远也不会用到。他与普通的受检异常相比没有任何益处,只会困扰API的用户。
总而言之,对于可恢复的情况,使用受检的异常;对于程序错误,则使用运行时异常。当然,情况并不总是那么黑白分明,例如,考虑资源枯竭的情形,这可能是由于程序错误而引起的,比如分配了一块不合理的过大的数组,也可能却是是由于资源不足而引起。如果资源枯竭是由于临时的短缺,或是临时需求太大所造成的,这种情况可能就是可恢复的。API设计者需要判断这样的资源枯竭是否允许恢复。如果你相信一种情况可能允许恢复,就使用受检的异常;如果不是,则使用运行时异常。如果不清楚是否有可能恢复,最好使用未受检的异常。
API的设计者往往会忘记,异常也是个完全意义上的对象,可以在他上面定义任意的方法。这些方法的主要用途是为捕获异常的代码而提供额外的信息,特别是关于引发这个异常条件的信息。如果没有这样的方法,程序员必须要懂得如何解析“该异常的字符串表示法”,以便获得这些额外信息。这是极为不好的做法。类很少会指定他们的字符串表示法中的细节,因此,不同的实现,不同的版本,字符串表示法会大相径庭。因此,“解析一场的字符串表示法”的代码可能是不可移植的,也是非常脆弱的。
因为受检的异常往往指明了可恢复的条件,所以,对于这样的异常,提供一些辅助方法尤其重要,通过这些方法,调用者可以获得一些有助于恢复的信息。例如,假设因为用户没有存储足够数量的钱,他企图在一个收费电话上进行呼叫就会失败,于是抛出受检的异常。这个异常应该提供一个访问方法,以便允许客户查询所缺的费用金额,从而可以将这个数值传递给电话用户。
建议:对可恢复的情况使用受检异常,对编程错误使用运行时异常。
最新推荐文章于 2023-01-07 23:53:49 发布