Walt You - 行是知之始

《Effective Java》学习日志(九)70:对可恢复条件使用受检异常,对编程错误使用运行异常

2019-01-08


学习资料主要参考: 《Effective Java Third Edition》,作者:Joshua Bloch



Java提供了三种throwable:checked exception,runtime exceptions和errors。 程序员之间存在一些混淆,即何时适合使用各种throwable。 虽然决策并不总是明确,但有一些一般规则可以提供强有力的指导。

决定是使用已检查或未检查的异常的基本规则是:对可以合理预期调用者恢复的条件使用已检查的异常。通过抛出已检查的异常,可以强制调用者在catch子句中处理异常或将其向外传播。 因此,声明抛出方法的每个已检查异常都是API用户的强有力指示,即关联条件是调用该方法的可能结果。

通过将用户与检查的异常对话,API设计者提出了从条件中恢复的任务。 用户可以通过捕获异常并忽略它来忽略任务,但这通常是个坏主意(第77项)。

有两种未经检查的throwable:运行时异常和错误。 它们的行为是相同的:两者都是不需要的,通常不应该被捕获的抛弃物。 如果程序抛出未经检查的异常或错误,通常情况下无法进行恢复,继续执行会带来弊大于利。 如果程序没有捕获这样的throwable,它将导致当前线程停止并显示相应的错误消息。

使用运行时异常来指示编程错误。绝大多数运行时异常表示违反前提条件。违反前提条件的原因仅仅是客户端API无法遵守API规范建立的合同。例如,数组访问的契约指定数组索引必须介于0和数组长度减去1(包括1和1)之间。 ArrayIndexOutOfBoundsException指示违反了此前提条件。

这个建议的一个问题是,您是否正在处理可恢复的条件或编程错误并不总是很清楚。例如,考虑资源耗尽的情况,这可能是由编程错误引起的,例如分配不合理的大型阵列,或者是由于资源的真正短缺。如果资源耗尽是由暂时短缺或暂时需求增加引起的,那么这种情况很可能是可以恢复的。 API设计者的判断是资源耗尽的给定实例是否可能允许恢复。如果您认为某个条件可能允许恢复,请使用已检查的异常;如果不是,请使用运行时异常。如果不清楚是否可以进行恢复,则最好使用未经检查的异常,原因如第71项中所述。

虽然Java语言规范不要求它,但有一个强烈的约定,即保留错误以供JVM使用以指示资源缺陷,不变故障或其他使其无法继续执行的条件。鉴于几乎普遍接受这种约定,最好不要实现任何新的Error子类。因此,您实现的所有未经检查的throwable应该是RuntimeException的子类(直接或间接)。你不仅不应该定义 错误的子类,但AssertionError除外,你不应该抛出它们。

可以定义一个不是Exception,RuntimeException或Error的子类的throwable。 JLS没有直接处理这样的throwable,而是隐式指定它们表现为普通的检查异常(它们是Exception的子类,但不是RuntimeException)。所以你什么时候应该使用这样的野兽?总之,永远不会。与普通的已检查异常相比,它们没有任何好处,只会使您的API用户感到困惑。

API设计者经常忘记异常是完全成熟的对象,可以在其上定义任意方法。此类方法的主要用途是提供捕获异常的代码,其中包含有关导致抛出异常的条件的其他信息。在没有这样的方法的情况下,已知程序员解析异常的字符串表示以发现附加信息。这是非常糟糕的做法(第12项)。可抛出的类很少指定其字符串表示的细节,因此字符串表示可以从实现到实现和发布到发布不同。因此,解析异常的字符串表示的代码可能是不可移植且易碎的。

由于检查的异常通常表示可恢复的条件,因此对他们来说提供方法来提供信息以帮助呼叫者从异常情况中恢复尤为重要。 例如,假设由于资金不足而尝试使用礼品卡进行购买时,会抛出已检查的异常。 该异常应该提供一种访问器方法来查询不足量。 这将使呼叫者能够将金额转发给购物者。 有关此主题的更多信息,请参阅第75项。

总而言之,抛出可恢复条件的已检查异常和编程错误的未经检查的异常。 如有疑问,请抛出未经检查的异常。 不要定义既不是检查异常也不是运行时异常的任何throwable。 提供已检查异常的方法以帮助恢复。


Content