57腰吟、只針對異常的情況才使用異常
try {
int i = 0;
while(true)
range[i++].climb();
}catch(ArrayIndexOutOfBoundsException e) {
}
在這段代碼中无埃,當(dāng)循環(huán)企圖訪問數(shù)組邊界之外的元素時,拋出異常毛雇,以達到終止無限循環(huán)的目的嫉称。在這個代碼片段中,基于異常的循環(huán)模式不僅模糊了代碼的意圖灵疮,而且降低了性能(異常模式比標(biāo)準(zhǔn)模式慢的多)织阅。
異常應(yīng)該只用于異常的情況,不要將它們用于控制流震捣,也不要編寫迫使客戶端使用控制流的API荔棉。
58闹炉、對可恢復(fù)的情況使用受檢異常,對編程錯誤使用運行時異常
Java語言提供了三種可拋出結(jié)構(gòu)(throwable):「受檢的異橙笥#」剩胁、「運行時異常」和「error」祥国。
「受檢的異常」是程序可以處理的異常晾腔,如果拋出異常的方法本身不能處理它舌稀,那么方法的調(diào)用者就應(yīng)該去處理它,從而使程序恢復(fù)運行灼擂。例如壁查,噴墨打印機在打印文件時,如果紙用完或者墨水用完剔应,就會暫停打印睡腿,等待用戶添加打印紙或更換墨盒,如果用戶添加了打印紙或更換了墨盒峻贮,就能繼續(xù)打印席怪。
「運行時異常」是程序無法恢復(fù)運行的異常纤控,導(dǎo)致這種異常的原因通常是由于執(zhí)行了錯誤操作挂捻。一旦出現(xiàn)了錯誤操作,建議終止程序并仔細的debug船万,因此Java編譯器不檢查這種異常刻撒。
「error」通常是系統(tǒng)出現(xiàn)了不可控的錯誤,這個錯誤通常與程序無關(guān)耿导,一般不需要處理声怔。 運行時異常和error都是不可恢復(fù)的情形。
對于可恢復(fù)的情況舱呻,使用受檢的異常醋火;對于程序錯誤使用運行時異常。對于自定義的未受檢的拋出結(jié)構(gòu)不要繼承自Error箱吕,它應(yīng)該是RuntimeException的子類胎撇。
59、避免不必要的使用受檢的異常
過分使用受檢的異常會使API使用起來非常不便殖氏,影響API的靈活性晚树。
60、優(yōu)先使用標(biāo)準(zhǔn)異常
使用標(biāo)準(zhǔn)異常的好處有:使API更易學(xué)習(xí)和使用雅采、可讀性更強和高度的代碼重用爵憎。
常用的異常:
異常 | 使用場合 |
---|---|
IllegalArgumentException | 非null的參數(shù)值不正確 |
IllegalStateException | 對于方法調(diào)用而言慨亲,對象狀態(tài)不合適 |
NullPointerException | 參數(shù)為null |
IndexOutOfBoundsException | 下標(biāo)參數(shù)越界 |
ConcurrentModificationException | 在禁止并發(fā)修改的情況下,檢測到對象的并發(fā)修改 |
UnsupportedOperationException | 對象不支持用戶請求的方法 |
61宝鼓、拋出與抽象相對應(yīng)的異常
當(dāng)方法傳遞由低層方法拋出的異常時刑棵,方法所拋出的異常與它執(zhí)行的任務(wù)沒有明顯的關(guān)系,這往往使人困惑愚铡,也讓實現(xiàn)細節(jié)污染了高層的API蛉签。為了避免這個問題,更高層的實現(xiàn)應(yīng)該捕獲低層的異常沥寥,同時拋出按高層抽象進行解釋的異常碍舍。這種做法被稱為「異常轉(zhuǎn)譯」。
例如:AbstractSequentialList類中
/**
* Returns the element at the specified position in this list.
*
* @throw 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);
}
}
如果低層的異常對于調(diào)試導(dǎo)致高層異常的問題有幫助邑雅,可以使用「異常鏈」將低層的異常傳到高層的異常中片橡,并使用高層異常的方法(Throwable.getCause)來獲取低層異常。如:
try {
...
} catch (LowerLevelException e) {
throw new HigherLevelException(e);
}
總之淮野,如果不能阻止或處理來自低層的異常捧书,一般使用「異常轉(zhuǎn)譯」來保證所拋出的異常適合高層≈栊牵「異常鏈」:它允許拋出適當(dāng)?shù)母邔赢惓>桑瑫r又能捕獲底層的原因進行失敗分析。
62洞难、每個方法拋出的異常都要有文檔
要為編寫的每個方法所能拋出的每個異常建立文檔了嚎。為每個受檢異常提供單獨的throws子句,并利用@throws標(biāo)記記錄下拋出每個異常的條件廊营。不要使用throws關(guān)鍵字將未受檢的異常包含在方法的聲明中歪泳。
63、在細節(jié)消息中包含能捕獲失敗的信息
異常的字符串表示法主要是讓程序員或域服務(wù)人員來分析失敗的原因露筒,所以其應(yīng)該包含盡可能多的失敗信息呐伞,以便于分析。
為了確保在異常的細節(jié)消息中包含足夠的能捕獲失敗的信息慎式,一種做法是在異常的構(gòu)造器而不是字符串細節(jié)消息中引入這些信息伶氢。如:
/**
* Constructs an <code>IndexOutOfBoundsException</code> with the
* specified detail message.
* @param lowerBound the lowest legal index value
* @param upperBound the highest index value plus one
* @param index the actual index value
*/
public MyIndexOutOfBoundsException(int lowerBound, int upperBound, int index) {
super("Lower bound: " + lowerBound + ", Upper bound: " + upperBound +
", Index: " + index);
}
//使用
....
throw new MyIndexOutOfBoundsException(0, 10, i);
64、努力使失敗保持原子性
一般而言瘪吏,為了能從異常中恢復(fù)癣防,失敗的方法調(diào)用應(yīng)該使對象保持在被調(diào)用之前的狀態(tài),稱這中方法具有「失敗原子性」掌眠。
對于不可變對象蕾盯,它具有失敗原子性是顯然的。因為對象的狀態(tài)始終保持一致蓝丙。
對于可變對象獲得失敗原子性最常見的方法:在執(zhí)行操作之前檢查參數(shù)的有效性级遭,這可以使對象的狀態(tài)在被修改之前望拖,先拋出適當(dāng)?shù)漠惓!H纾?/p>
public Object pop() {
if(size == 0)
throw new EmptyStackException();
Object result = elements[--size];
....
}
總之挫鸽,產(chǎn)生任何異常都應(yīng)該讓對象保持在方法調(diào)用之前的狀態(tài)说敏。若違反了這條規(guī)則,API文檔中應(yīng)該清楚的指明對象處于什么樣的狀態(tài)丢郊。
65盔沫、不要忽略異常
用空的catch塊來忽略異常,可能會產(chǎn)生災(zāi)難性的后果枫匾。永遠也不要忽略異常架诞。