Tips
书中的源代码地址: https://github.com/jbloch/effective-java-3e-source-code
注意, 书中的有些代码里方法是基于 Java 9 API 中的, 所以 JDK 最好下载 JDK 9 以上的版本.
Effective Java, Third Edition
73. 抛出合乎于抽象的异常
当一个方法抛出一个与它所执行的任务没有明显关联的异常时, 这是令人不安的. 在方法传播由低层 (lower-level) 抽象抛出的异常时, 会经常发生这种情况. 它不仅令人不安, 而且用实现细节 "污染" 了上层的 API. 如果上层 (higher layer) 的实现在以后的版本中发生变化, 那么它抛出的异常也会发生变化, 可能会破坏现有的客户端程序.
为了避免这个问题, 上层 (higher layers) 应该捕获低层 ( lower-level ) 的异常, 并在它们的位置抛出可以用上层级别 (higher-level ) 抽象来解释的异常. 这个习语被称为异常转译:
- // Exception Translation
- try {
- ... // Use lower-level abstraction to do our bidding
- } catch (LowerLevelException e) {
- throw new HigherLevelException(...);
- }
以下的异常转转译的示例是来自 AbstractSequentialList 类, 该类是 List 接口的骨架实现(skeletal implementation )(条目 20). 在此示例中, 异常转译由 List <E > 接口中的 get 方法规范强制要求的:
- /**
- * Returns the element at the specified position in this list.
- * @throws IndexOutOfBoundsException if the index is out of range
- * ({@code index <0 || index>= size()}).
- */
- public E get(int index) {
- ListIterator<E> i = listIterator(index);
- try {
- return i.next();
- } catch (NoSuchElementException e) {
- throw new IndexOutOfBoundsException("Index:" + index);
- }
- }
如果较低级别的异常可能有助于调试导致较高级别异常的问题, 则需要一种称为异常链 (exception chaining ) 的特殊异常转译形式. 低层异常 (原因) 传递给高层异常, 高层异常提供一个访问器方法 (Throwable 的 getCause 方法) 来检索低层异常:
- // Exception Chaining
- try {
- ... // Use lower-level abstraction to do our bidding
- } catch (LowerLevelException cause) {
- throw new HigherLevelException(cause);
- }
高级异常的构造方法将原因传递给一个感知链 (chaining-aware) 的父类构造方法, 因此它最终被传递给 Throwable 的一个感知链的构造方法, 比如 Throwable(Throwable):
- // Exception with chaining-aware constructor
- class HigherLevelException extends Exception {
- HigherLevelException(Throwable cause) {
- super(cause);
- }
- }
大多数标准异常都有感知链的构造方法. 对于没有这样做的异常, 可以使用 Throwable 的 initCause 方法设置原因. 异常链接不仅允许你以编程方式访问原因(使用 getCause), 而且还将原因的堆栈跟踪集成到更高级别异常的堆栈跟踪中.
虽然异常转译优于低层异常的无意识传播, 但不应过度使用. 在可能的情况下, 处理较低层异常的最佳方法是通过确保较低级别的方法成功执行来避免异常. 有时可以通过检查更高级别方法的参数的有效性, 然后再将它们传递到较低层来完成此操作.
如果不可能防止来自较低层的异常, 那么接下来最好的事情就是让较高层静默地解决这些异常, 从而使较高级别方法的调用者与较低级别的问题隔离开来. 在这些情况下, 使用某些适当的日志记录工具 (如 java.util.logging) 记录异常可能是适当的. 这允许程序员调查问题, 同时把使用者和客户端代码隔离开.
总之, 如果无法阻止或处理较低层的异常, 那么使用异常转译, 除非较低级别的方法恰好保证其所有异常都适用于较高级别. 异常链接提供了两全其美的优势: 它允许抛出适当的更高级别异常, 同时可以捕获失败分析的根本原因(条目 75).
来源: http://www.jianshu.com/p/f33afa9c801b