当一个方法抛出一个与它所执行的任务没有明显关联的异常时,这是令人不安的。这种情况经常发生在方法传播由低层抽象抛出的异常时。它不仅令人不安,而且用实现细节污染了上层的API。如果高级层的实现在以后的版本中发生变化,那么它抛出的异常也会发生变化,可能会破坏现有的客户机程序。
为了避免这个问题,较高的层应该捕获较低级别的异常,并在它们的位置抛出可以用较高级别抽象解释的异常。这个习语被称为异常翻译:
下面是一个从AbstractSequentialList类,它是列表接口的一个框架实现( item20 )。在这个例子中,异常转换由List<E>的get方法的规范强制执行:
如果低级异常可能有助于调试导致高级异常的问题,则需要一种称为异常链接的特殊异常转换形式。较低级别的异常(原因)被传递给更高级别的异常,它提供了一个访问器方法
(Throwable的getCause方法)来检索低层异常:
:高级异常的构造函数将原因传递给一个感知链的超类构造函数,所以它最终被传递给Throwable的一个感知链的构造函数,比如Throwable(Throwable):
大多数标准异常都有感知链的构造函数。对于没有这样做的异常,可以使用Throwable的initCause方法设置原因。异常链接不仅允许您以编程方式访问原因(使用getCause),而且还将原因的堆栈跟踪集成到更高级别异常的堆栈跟踪中。
虽然异常翻译优于底层异常的盲目传播,但它不应该被过度使用。在可能的情况下,处理较低层异常的最佳方法是通过确保较低层方法成功避免异常。有时,您可以在将高级方法的参数传递到较低的层之前检查它们的有效性。
如果不可能从较低的层防止异常,那么下一个最好的方法就是让较高的层安静地处理这些异常,使较高级别方法的调用者免受较低级别问题的影响。在这些情况下,可以使用一些适当的日志工具(例如)记录异常例如java.util.logging. 这允许程序员研究问题,同时将客户机代码和用户与之隔离。
总之,如果无法防止或处理来自较低层次的异常,则使用异常转换,除非较低层次的方法恰好保证其所有异常都适用于较高层次。链接提供了两种方法的优点:它允许您抛出适当的高级异常,同时捕获失败分析的潜在原因( item75 )。
本文写于2019.7.22,历时1天