Effective Java 第三版——73.拋出合乎於抽象的異常

Tips
書中的源代碼地址:https://github.com/jbloch/effective-java-3e-source-code
注意,書中的有些代碼裏方法是基於Java 9 API中的,因此JDK 最好下載 JDK 9以上的版本。java

Effective Java, Third Edition

73. 拋出合乎於抽象的異常

當一個方法拋出一個與它所執行的任務沒有明顯關聯的異常時,這是使人不安的。在方法傳播由低層(lower-level)抽象拋出的異常時,會常常發生這種狀況。它不只使人不安,並且用實現細節「污染」了上層的API。若是上層(higher layer)的實如今之後的版本中發生變化,那麼它拋出的異常也會發生變化,可能會破壞現有的客戶端程序。git

爲了不這個問題,上層(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方法規範強制要求的:github

/**
 * 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),並且還將緣由的堆棧跟蹤集成到更高級別異常的堆棧跟蹤中。this

雖然異常轉譯優於低層異常的無心識傳播,但不該過分使用。 在可能的狀況下,處理較低層異常的最佳方法是經過確保較低級別的方法成功執行來避免異常。 有時能夠經過檢查更高級別方法的參數的有效性,而後再將它們傳遞到較低層來完成此操做。調試

若是不可能防止來自較低層的異常,那麼接下來最好的事情就是讓較高層靜默地解決這些異常,從而使較高級別方法的調用者與較低級別的問題隔離開來。 在這些狀況下,使用某些適當的日誌記錄工具(如java.util.logging)記錄異常多是適當的。 這容許程序員調查問題,同時把使用者和客戶端代碼隔離開。日誌

總之,若是沒法阻止或處理較低層的異常,那麼使用異常轉譯,除非較低級別的方法剛好保證其全部異常都適用於較高級別。 異常連接提供了一箭雙鵰的優點:它容許拋出適當的更高級別異常,同時能夠捕獲失敗分析的根本緣由(條目 75)。code

相關文章
相關標籤/搜索