119 Star 1K Fork 296

lin-mt / effective-java-third-edition

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
第73项:抛出与抽象相对应的异常.md 4.31 KB
一键复制 编辑 原始数据 按行查看 历史
lin-mt 提交于 2019-12-26 22:04 . 修订73、74项

抛出与抽象相对应的异常

  如果方法抛出的异常与它所执行的任务没有明显的联系,这种情形将会使人不知所措。当方法传递由底层抽象抛出的异常时,往往会发生这种情况。除了使人感到困惑之外,这也让实现细节污染了更高层的 API。如果高层的实现在后续的发行版本中发生了变化,它所抛出的异常也可能会跟着发生变化,从而潜在地破坏现有的客户端程序。

  为了避免这个问题,更高层的实现应该捕获底层的异常,同时抛出可以按照高层抽象进行解释的异常 。这种做法被称为异常转译(exception translation)

// Exception Translation
try {
    ... // Use lower-level abstraction to do our bidding
} catch (LowerLevelException e) {
    throw new HigherLevelException(...);
}

  下面的异常转义例子取自 AbstractSequentialList 类,该类是 List 接口的一个*骨架实现(skeletal implementation)(第 20 项)。在这个例子中,按照 List接口中 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 项)。

其他
1
https://gitee.com/lin-mt/effective-java-third-edition.git
git@gitee.com:lin-mt/effective-java-third-edition.git
lin-mt
effective-java-third-edition
effective-java-third-edition
master

搜索帮助