effective-java-3rd-chinese/docs/notes/70. 对可恢复的情况使用受检异常,对编程错误使用运行时异常.md
2019-03-14 13:10:17 +08:00

25 lines
5.1 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 70. 对可恢复的情况使用受检异常,对编程错误使用运行时异常
  Java 程序设计语言提供了三种 throwable受检异常checked exceptions、运行时异常runtime exceptions和错误errors。程序员中存在着什么情况适合使用哪种 throwable 的困惑。虽然这种决定不总是那么清晰,但还是有一些一般性的原则提出了强有力的指导。
  在决定使用受检异常还是非受检异常时,主要的原则是: **如果期望调用者能够合理的恢复程序运行,对于这种情况就应该使用受检异常。** 通过抛出受检异常,强迫调用者在一个 catch 子句中处理该异常,或者把它传播出去。因此,方法中声明要抛出的每个受检异常都是对 API 用户的一个潜在提示:与异常相关联的条件是调用这个方法一种可能结果。
  API 的设计者让 API 用户面对受检异常,以此强制用户从这个异常条件条件中恢复。用户这可以忽视这样的强制要求,只需要捕获异常即可,但这往往不是个好办法(详见第 77 条)。
  有两种非受检的 throwable运行时异常和错误。在行为上两者是等同的它们都是不需要也不应该被捕获的 throwable。如果程序抛出非受检异常或者错误往往属于不可恢复的情形程序继续执行下去有害无益。如果程序没有捕捉到这样的 throwable将会导致当前线程中断halt并且出现适当的错误消息。
  **用运行时异常来表明编程错误**。大多数运行时异常都表示前提违例precondition violations。所谓前提违例是指 API 的客户没有遵守 API 规范建立的约定。例如,数组访问的预定指明了数组的下标值必须在 0 和数组长度-1 之间。ArrayIndexOutOfBoundsException 表明违反了这个前提。
  这个建议有一个问题对于要处理可恢复的条件还是处理编程错误情况并非总是那么黑白分明。例如考虑资源枯竭的情形这可能是由程序错误引起的比如分配了一块不合理的过大数组也可能确实是由于资源不足而引起的。如果资源枯竭是由于临时的短缺或是临时需求太大造成的这种情况可能是可恢复的。API 设计者需要判断这样的资源枯竭是否允许恢复。如果你相信一种情况可能允许回复,就使用受检异常;如果不是,则使用运行时异常。如果不清楚是否有可能恢复,最好使用非受检异常,原因参见 71 条。
  虽然 JLSJava 语言规范并没有要求但是按照惯例错误Error往往被 JVM 保留下来使用,以表明资源不足、约束失败,或者其他使程序无法继续执行的条件。由于这已经是个几乎被普遍接受的管理,因此最好不需要在实现任何新的 Error 的子类。因此,**你实现的所有非受检的 throwable 都应该是 RuntimeExceptiond 子类**(直接或者间接的)。不仅不应该定义 Error 的子类,也不应该抛出 AssertionError 异常。
  要想定义一个不是 Exception、RuntimeException 或者 Error 子类的 throwable这也是有可能的。JLS 并没有直接规定这样的 throwable而是隐式的指定了从行为意义上讲他们等同于普通的受检异常即 Exception 的子类,但不是 RuntimeException 的子类)。那么什么时候应该使用这样的 throwable一句话永远也不会用到。它与普通的受检异常相比没有任何益处还会困扰 API 的使用者。
  API 的设计者往往会忘记,异常也是一个完全意义上的对象,可是在它上面定义任何的方法。这些方法的主要用途是捕获异常的代码提供额外信息,特别是关于引发这个异常条件的信息。如果没有这样的方法,程序员必须要懂的如何解析“该异常的字符串表示法”,以便获得这些额外信息。这是极为不好的做法(详见 12 条)。类很少会指定它们的字符串表示法中的细节,因此对于不同的实现及不同的版本,字符串表示法也会大相径庭。由此可见,“解析异常的字符串表示法”的代码可能是不可移植的,也是非常脆弱的。
  因为受检异常往往指明了可恢复的条件,所以对于这样的异常,提供一些辅助方法尤其重要,通过这种方法调用者可以获得一些有助于程序恢复的信息。例如,假设因为用户资金不足,当他企图购买一张礼品卡时导致失败,于是抛出受检异常。这个异常应该提供一个访问方法,以便允许客户查询所缺的费用金额,使得使用者可以将这个数值传递给用户。关于这个主题的更多详情,参见 75 条。
  总而言之,对于可恢复的情况,要抛出受检异常;对于程序错误,就要抛出运行时异常。不确定是否可恢复,就跑出为受检异常。不要定义任何既不是受检异常也不是运行异常的抛出类型。要在受检异常上提供方法,以便协助程序恢复。