聲明:原創作品,轉載時請注明文章來自SAP師太技術博客( 博/客/園www.cnblogs.com):m.survivalescaperooms.com/jiangzhengjun,并以超鏈接形式標明文章原始出處,否則將追究法律責任!原文鏈接:http://m.survivalescaperooms.com/jiangzhengjun/p/4255688.html 第九章 異常 57、 只針對異常的情況才使用異常 也許你在將來會碰到下面這樣的代碼,它是基本異常模式的循環:
try{
int i = 0;
while(true)
range[i++].climb();
}catch(ArrayIndexOutOfBoundsException e){
}
這所以有些人會這么做,是因為他們企圖利用Java的錯誤判斷機制來提高性能,因為VM對每次數組訪問都要檢查越界情況,即使是使用for-each,他們認為正常的循環終止測試只是被編譯器隱藏了,但在循環中仍然可見,這種考慮無疑是多余的。
實例上,在現代的JVM實例上,基本異常的模式比標準模式要慢得多。
異常應該只用于異常的情況下,它們永遠不應該用于正常的控制流。
設計良好的API不應該強迫它的客戶端為了正常的控制流程而使用異常。如Iterator接口有一個“狀態相關”的next方法,和相應的狀態測試方法hasNext,這使得利用傳統的for循環對集合進行迭代的標準模式成為可能:
for(Iterator<Foo> i = coolection.iterator(); i.hasNext()){
Foo foo = i.next();
…
}
如果Iterator缺少hasNext方法,客戶端將被迫改用下面的做法:
try{
Iterator<Foo> i = collection.iterator();
while(true){
Foo foo = i.next();
…
}
}catch(NoSuchElementException e){
}
另一種提供單獨的狀態測試方法的做法是,如果“狀態相關的”方法被調用時,該對象處于不適當的狀態之中時,它就會返回一個可識別的值,比如null。這種方法對于Iterator而言不合適,因為null是next就去的合法返回值。
對于“狀態測試方法”和“可識別的返回值”這兩種做法,有些告誡:如果對象在缺少外部同步的情況下被并發訪問,或者可被外界改變狀態,使用可被識別的返回值可能是很有必要的,因為在調用“狀態測試”方法和調用對應的“狀態相關”方法的時間間隔之中,對象的狀態有可能會發生變化。但從性能角度考慮,使用可被識別的返回值要好些。如果所有其他方面都是等同的,那么“狀態測試”方法則略優先可被識別的返回值。
總之,異常是為了在異常情況下使用而設計的,不要將它們用于普通的控制流,也不要縮寫迫使它們這么做的API。
>>>《PRactice Java》異常拾遺<<<
如果finally塊中出現了異常沒有捕獲或者是捕獲后重新拋出,則會覆蓋掉try或catch里拋出的異常,最終拋出的異常是finally塊中產生的異常,而不是try或catch塊里的異常,最后會丟失最原始的異常。
如果在try、catch、finally塊中都拋出了異常,只是只有一個異常可被傳播到外界。記住,最后被拋出的異常是唯一被調用端接受到的異常,其他異常都被掩蓋而后丟失掉了。如果調用端需要知道造成失幾的初始原因,程序之中就絕不能掩蓋任何異常。
請不要在try塊中發出對return、break或continue的調用,萬一無法避免,一定要確保finally的存在不會改變函數的返回值(比如說拋異常啊、return啊以及其他任何引起程序退出的調用)。因為那樣會引起流程混亂或返回值不確定,如果有返回值最好在try與finally外返回。
不要將try/catch放在循環內,那樣會減慢代碼的執行速度。
如果構造器調用的代碼需要拋出異常,就不要在構造器處理它,而是直接在構造器聲明上throws出來,這樣更簡潔與安全。因為如果在構造器里處理異常或將產生異常的代碼放在構造器之外調用,都將會需要調用額外的方法來判斷構造的對象是否有效,這樣可能忘記調用這些額外的檢查而不安全。
58、 對可恢復的情況使用受檢異常,對編程錯誤使用運用時異常Java程序設計語言提供了三種異常:受檢的異常(checked exception)、運行時異常(run-time exception)和錯誤(error)。關于什么時候適合使用哪種異常,雖然沒有明確的規定,但還是有些一般性的原則的。
檢測性異常通常是由外部條件不滿足而引起的,只要條件滿足,程序是可以正常運行的,即可在不修改程序的前提下就可正常運行;而運行時異常則是由于系統內部或編程時人為的疏忽而引起的,這種異常一定要修正錯誤代碼后再能正確運行。受檢異常對客戶是有用的,而運行時異常則是讓開發人員來調試的,對客戶沒有多大的用處。
在決定使用受檢異常還是未受檢異常時,主要的原則是:如果期望調用者能夠適當地恢復,對于這種情況就應該使用受檢的異常。拋出的受檢異常都是對API用戶的一種潛在的指示:與異常相關的條件是調用這個方法的一種可能的結果。
有兩種未受檢的異常:運行時異常和錯誤。在行為上兩種是等同:它們都不需要捕獲。如果拋出的是未受檢異常或錯誤,往往就屬于不可恢復的情形,繼續執行下去有害無益。如果程序未捕獲這樣的異常或錯誤,將會導致線程停止,并出現適當的錯誤消息。
用運行時異常來表明編程錯誤。大多數的運行時異常都表示違返了API規約,API的客戶同有遵守API規范。例如,數組訪問的約定指明了數組的下標值必須在零和數組長度減1之間,ArrayIndexOutOfBoundsException表明了這個規定。
按照慣例,錯誤往往被JVM保留用于表示資源不足、約束失敗,或者其他程序無法繼續執行的條件。由于這已經是個幾乎被普遍接受的慣例,因此最好不要再實現任何新的Error子類。因此,你實現的所有未受檢異常都應該是RuntimeException的子類或間接是的。
總而言這,對于可恢復的情況,使用受檢的異常;對于程序錯誤,則使用運行時異常。當然,這也不總是這么分明的。例如,考慮資源枯竭的情形,這可能是由于程序錯誤而引起的,比如分配了一塊不合理的過大的數組,也可能確實是由于資源不足而引起的。如果資源枯竭是由于臨時的短缺,或是臨時需求太大所造成的,這種情況可能就是可恢復的。API設計者需要判斷這樣的資源枯竭是否允許。如果你相信可允許恢復,就使用受檢異常,否則使用運行時異常。如果不清楚,最好使用未受檢異常(原因請見第59條)。
API的設計都往往會忘記,異常也是個完全意義上的對象,可以在它上面定義任意的方法,這些方法的主要用途是為捕獲異常的代碼而提供額外的信息,特別是關于引發這個異常條件的信息。如果沒有這樣的方法,API用戶必須要懂得如何解析“該異常的字符串表示法”,以便獲得這些額外的信息,這是極不好的作法。類很少以文檔的形式指定它們的字符串表示的細節,因此,不同的實現,不同的版本,字符串表示可能會大相徑庭,因此,“解析異常的字符串表示法”的代碼可能是不可移植的,也是非常脆弱的。
因為受檢異常往往指明了可恢復的條件,所以,這于這樣的異常,提供一些輔助方法尤其重要,通過這些方法,調用都可以獲得一些有助于恢復的信息。例如,假設因為沒有足夠的錢,他企圖在一個收費電話上呼叫就會失敗,于是拋出檢查異常。這個異常應該提供一個訪問方法,以便用戶所缺的引用金額,從而可以將這個數組傳遞給電話用戶。
59、 避免不必要地使用受檢異常受檢異常與運行時異常不一樣,它們強迫程序員處理異常的條件,大大增強了可靠性,但過分使用受檢異常會使用API使用起來非常不方便。如果方法拋出一個或者多個受檢異常,調用都就必須在一個或多個catch塊中處理,或者將它們拋出并傳播出去。無論是哪種,都會給程序員添加不可忽視的負擔。當然,如果程序員能處理這一類異常,則不算做負擔,但以下決對是負擔:
}catch(TheCheckedException e){
throw new AssertionError();//斷言不會發生異常
}
或
}catch(TheCheckedException e){//哦,失敗了,不能恢復
e.printStackTrace();
System.exit(1);
}
上面兩種方式都不是最好的處理方式,之所以程序員這樣處理,是因為他們認為這根本就是一種不可恢復的異常(當然程序員這種認為要是正當的),如果使用API的程序員無法做得比這更好,那么未受檢異常可能更為合適,所以異常設計者應將TheCheckedException設計成運行時異常會更好些。這種例子的反例就是CloneNotSupportedException,它是Object.clone拋出來的,而Object.clone應該只是在實現了Cloneable的對象上者可以被調用,這顯然是API調用都未實現該接口所導致,除非程序實現該接口,否是不可恢復的,所以CloneNotSupportedException應該設計成運行時異常或許更加合理一些。
如果方法只拋出單個受檢異常,也會導致該方法不得在try塊中,在這種情況下,應該問自己,是否有別的途徑來避免API調用者使用受檢的異常。這里提供這樣的參考,我們可以把拋出的單個異常的方法分成兩個方法,其中一個方法返回一個boolean,表明是否該拋出異常。這種API重構,把下面的調用:
try{//調用時檢查異常
obj.action(args);//調用檢查異常方法
}catch(TheCheckedExcption e){
//處理異常條件
...
}
重構為:
if(obj.actionPermitted(args)){//使用狀態測試方法消除catch
obj.action(args);
}else{
//處理異常條件
...
}
這種重構并不總是合適的,但在合適的地方,它會使用API用起來更加舒服。雖然沒有前者漂亮,但更加靈活——如果程序員知道調用肯定會成功,或不介意由調用失敗而導致的線程終止,則下面為理為簡單的調用形式:
obj.action(args);
如果你懷疑這個簡單的調用是否成功,那么這個API重構則可能就是恰當的。這種重構之后的API在本質上等同于第57條件的“狀態測試方法”,并且,同樣的告誡也要遵循(告誡參考57)。
60、 優先使用標準異常java平臺提供了一組基本未受檢查的異常,它們滿足了絕大多數API的異常拋出的需要。
重用現有的異常好處最主要的是,易學和易使用,與已經熟悉的習慣用法一致。第二可讀性更好,不會出現不熟悉的異常。
最經常被重用的異常是IllegalArgumentException。當調用者傳遞的參數值不合適的時候,往往就會拋出這個異常。如需要一個正數而傳遞的是一個負數時。
另一個經常被重用的異常是IllegalStateException。如果因為對象的狀態而使用調用非法,則會拋出。如某個對象被正確初始化之前就調用。
Word-spacing: 0px; text-transform: none; word-break: normal; margin: 0cm 0cm 0pt; l
新聞熱點
疑難解答