ITEM 70: 对可恢复条件使用检查异常,对编程错误使用运行时

2020-06-26  本文已影响0人  rabbittttt

ITEM 70: USE CHECKED EXCEPTIONS FOR RECOVERABLE CONDITIONS AND RUNTIME EXCEPTIONS FOR PROGRAMMING ERRORS

  Java提供了三种 Throwable:检查异常、运行时异常和错误。程序员对于什么时候适合使用每种throwable 有一些混淆。虽然决定并不总是明确的,但有些一般性的规则可以提供强有力的指导。
  决定是否使用检查异常或未检查异常的基本规则是:对调用者可以合理地期望恢复的条件使用检查异常。通过抛出检查异常,可以强制调用者在catch子句中处理异常,或者向外传播异常。因此,方法声明要抛出的每个检查异常都有力地向API用户表明,关联的条件是调用该方法的可能结果。
  通过让用户面对一个检查过的异常,API设计人员要求从该条件中恢复。用户可以通过捕获并忽略异常来忽略这个要求,但这通常不是一个好主意(item 77)。
  有两种未检查的可掷性:运行时异常和错误。它们在行为上是相同的:它们都是不需要也通常不应该被捕获的可抛掷对象。如果程序抛出未检查的异常或错误,通常情况下恢复是不可能的,继续执行将弊大于利。如果程序没有捕获这样的throwable,它将导致当前线程停止,并给出相应的错误消息。
  使用运行时异常来指示编程错误。绝大多数运行时异常都表示违反了先决条件。前提条件违反仅仅是API的客户端没有遵守API规范所建立的契约。例如,数组访问契约规定数组索引必须在0和数组长度- 1之间。ArrayIndexOutOfBoundsException 表示违反了这个先决条件。
  这个规范存在一个问题,可恢复的条件还是编程错误的边界并不总是很清楚。例如,考虑资源耗尽的情况,这可能是由编程错误(比如分配了一个不合理的大数组)或资源真正短缺造成的。如果资源耗尽是由暂时的短缺或暂时增加的需求造成的,这种情况很可能是可恢复的。
  一个给定的资源耗尽实例是否允许恢复是由 API 设计人员判断的问题。如果您认为某个条件可能允许恢复,则使用已检查的异常;如果没有,使用运行时异常。如果不清楚是否可以恢复,您最好使用未检查异常,原因见 item 71。
  虽然Java语言规范并不要求这样做,但是有一个严格的约定,即 JVM 使用 errors 以表示资源不足、不变故障或其他使其无法继续执行的条件。鉴于这种约定几乎被普遍接受,最好不要实现任何新的错误子类。因此,您实现的所有未检查的 throwables 都应该(直接或间接)继承 RuntimeException。不仅不应该定义错误的 Error 子类,而且除了AssertionError 之外,也不应该抛出它们。
  可以定义一个非 Exception、RuntimeException或Error的子类 的 throwable。JLS不直接处理这种异常,但隐式指定它们作为普通的检查异常(它们是 Exception 的子类,而不是 RuntimeException) 进行行为。那么,什么时候应该使用这种方式呢?最好永远不要。它们与普通的受控异常相比没有什么好处,只会使API的用户感到困惑。
  API设计人员经常忘记,异常是可以定义任意方法的成熟对象。这类方法的主要用途是提供带有关于引发异常的条件的附加信息的捕获异常的代码。
  如果没有这样的方法,程序员就会解析异常的字符串表示,以找出额外的信息。这是非常糟糕的做法(item 12)。可抛掷类很少指定其字符串表示的细节,因此字符串表示形式可能因实现和版本而异。因此,解析异常的字符串表示的代码很可能是不可移植并且脆弱的。
  由于已检查的异常通常表示可恢复条件,因此可以利用它们提供的信息来帮助调用者从异常条件中恢复。例如,当使用礼品卡进行购买,由于资金不足而失败时,会抛出一个检查异常。异常应该提供一个访问器方法来查询差额的数量。这将使调用者能够将数量转发给购物者。关于这个话题的更多信息见item 75。
  总而言之,对于可恢复条件抛出检查过的异常,对于编程错误抛出未检查过的异常。当有疑问时,抛出未检查的异常。不要定义任何既不是检查异常也不是运行时异常的可掷性。为已检查异常提供方法以帮助进行恢复。

上一篇下一篇

猜你喜欢

热点阅读