如果方法拋出的異常與它所執(zhí)行的任務(wù)沒有明顯的聯(lián)系,這種情形物會使人不知所措。當(dāng)方法傳遞由低層抽象拋出的異常時,往往會發(fā)生這種情況。除了使人感到困惑之外,這也讓實現(xiàn)細節(jié)污染了更高層的API。如果高層的實現(xiàn)在后續(xù)的發(fā)行版本中發(fā)生了變化,它所拋出的異常也可能會跟著發(fā)生變化,從而潛在地破壞現(xiàn)有的客戶端程序。
為了避免這個問題,更高層的實現(xiàn)應(yīng)該捕獲低層的異常,同時拋出可以按照高層抽象進行解釋的異常。這種做法被稱為異常轉(zhuǎn)譯(exceptiontranslation),如下所示:
try{
//Use lower-level abstraction to do our bidding
...
}cache(LowerLevelException e){
throw new HigherLevelException(...);
}
下面的異常轉(zhuǎn)譯例子取自于AbstractSequentialList類,該類是List接口的一個骨架實現(xiàn)(skeletal implementation)(見第18條)。在這個例子中,按照List<E>接口中g(shù)et方法的規(guī)范要求,異常轉(zhuǎn)譯是必需的:
/**
* Returns the element at the specified position in this list.
*
* <p>This implementation first gets a list iterator pointing to the
* indexed element (with <tt>listIterator(index)</tt>). Then, it gets
* the element using <tt>ListIterator.next</tt> and returns it.
*
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
try {
return listIterator(index).next();
} catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index);
}
}
一種特殊的異常轉(zhuǎn)譯形式稱為異常鏈(exceptionchaining),如果低層的異常對于調(diào)試導(dǎo)致髙層異常的問題非常有幫助,使用異常鏈就很合適。低層的異常(原因)被傳到髙層的異常,髙層的異常提供訪問方法(Thmwable.getCause)來獲得低層的異常:
try{
... //Use lower-level abstraction to do our bidding
}cache(LowerLevelException cause){
throw new HigherLevelException(cause);
}
髙層異常的構(gòu)造器將原因傳到支持鏈(chaining-aware)的超級構(gòu)造器,因此它最終將被傳給Throwable的其中一個運行異常鏈的構(gòu)造器,例如Throwable(Throwable):
// Exception with chaining-aware constructor
class HigherLevelException extends Exception {
HigherLevelExceptionCThrowable cause) {
super(cause);
}
}
大多數(shù)標準的異常都有支持鏈的構(gòu)造器。對于沒有支持鏈的異常,可以利用Thmwable的initCause方法設(shè)置原因。異常鏈不僅讓你可以通過程序(用getCause)訪問原因,它還可以將原因的堆棧軌跡集成到更髙層的異常中。
盡管異常轉(zhuǎn)譯與不加選擇地從低層傳遞異常的做法相比有所改進,但是它也不能被濫用。如有可能,處理來自低層異常的最好做法是,在調(diào)用低層方法之前確保它們會成功執(zhí)行,從而避免它們拋出異常。有時候,可以在給低層傳遞參數(shù)之前,檢査更高層方法的參數(shù)的有效性,從而避免低層方法拋出異常。
如果無法避免低層異常,次選方案是,讓更髙層來悄悄地繞開這些異常,從而將高層方法的調(diào)用者與低層的問題隔離開來。在這種情況下,可以用某種適當(dāng)?shù)挠涗洐C制(如java.util.logging)將異常記錄下來。這樣有助于管理員調(diào)査問題,同時又將客戶端代碼和最終用戶與問題隔離開來。
總而言之,如果不能阻止或者處理來自更低層的異常,一般的做法是使用異常轉(zhuǎn)譯,除非低層方法碰巧可以保證它拋出的所有異常對髙層也合適才可以將異常從低層傳播到高居。異常鏈對髙層和低層異常都提供了最佳的功能:它允許拋出適當(dāng)?shù)捏{層異常,同時又能捕獲底層的原因進行失敗分析(見第63條)。
附:異常轉(zhuǎn)譯和異常鏈資料:http://blog.csdn.net/dichyzhu/article/details/3871635