effective-java-3rd-chinese/docs/notes/73. 抛出与抽象对应的异常.md
2019-05-27 22:08:19 +08:00

62 lines
4.0 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.

# 73. 抛出与抽象对应的异常
  如果方法抛出的异常与它所执行的任务没有明显的联系,这种情形将会使人不知所措。当方法传递由低层抽象抛出的异常时,往往会发生这种情况。除了使人感到困惑之外,这也“污染”了具有实现细节的更高层的 API 。如果高层的实现在后续的发行版本中发生了变化,它所抛出的异常也可能会跟着发生变化,从而潜在地破坏现有的客户端程序。
  为了避免这个问题, **更高层的实现应该捕获低层的异常,同时抛出可以按照高层抽象进行解释的异常。**这种做法称为异常转译 exception translation如下代码所示
```java
/* 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 方法的规范要求,异常转译是必需的:
```java
/**
* 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 方法)来获得低层的异常:
```java
// Exception Chaining
try {
... // Use lower-level abstraction to do our bidding
} catch (LowerLevelException cause) {
throw new HigherLevelException(cause);
}
```
  高层异常的构造器将原因传到支持链chaining-aware的超级构造器因此它最终将被传给 Throwable 的其中一个运行异常链的构造器,例如 Throwable(Throwable) :
```java
/* Exception with chaining-aware constructor */
class HigherLevelException extends Exception {
HigherLevelException( Throwable cause ) {
super(cause);
}
}
```
  大多数标准的异常都有支持链的构造器。对于没有支持链的异常,可以利用 Throwable 的 initCause 方法设置原因。异常链不仅让你可以通过程序(用 getCause访问原因还可以将原因的堆战轨迹集成到更高层的异常中。
  **尽管异常转译与不加选择地从低层传递异常的做法相比有所改进,但是也不能滥用它。** 如有可能,处理来自低层异常的最好做法是,在调用低层方法之前确保它们会成功执行,从而避免它们抛出异常。有时候,可以在给低层传递参数之前,检查更高层方法的参数的有效性,从而避免低层方法抛出异常。
  如果无法阻止来自低层的异常,其次的做法是,让更高层来悄悄地处理这些异常,从而将高层方法的调用者与低层的问题隔离开来。在这种情况下,可以用某种适当的记录机制(如 java.util.logging将异常记录下来。这样有助于管理员调查问题同时又将客户端代码和最终用户与问题隔离开来。
  总而言之,如果不能阻止或者处理来自更低层的异常,一般的做法是使用异常转译,只有在低层方法的规范碰巧可以保证“它所抛出的所有异常对于更高层也是合适的”情况下,才可以将异常从低层传播到高层。异常链对高层和低层异常都提供了最佳的功能:它允许抛出适当的高层异常,同时又能捕获低层的原因进行失败分析(详见第 75 条) 。