try{ int i = 0; while(true) range[i++].climb(); }catch(ArrayIndexOutOfBoundsException e){ }
企圖經過異常來終止循環,是對異常的誤用,異常是針對不正常狀況才使用的。上面的基於異常模式運行上,比正常的模式在性能上慢不少,2倍或以上。java
對於API的設計也是如此,不要強迫它的客戶端爲了正常的控制流而使用異常程序員
通常的處理方法有兩種:數組
若是對象的狀態是線程安全的話,推薦使用,例如安全
for(Iterator<Foo> i = collection.iterator();i.hasNext();){ Foo foo = i.next(); }
這裏的hasNext就是狀態監測併發
若是使用異常的話,客戶端就得這樣性能
try{ Iterator<Foo> i = collection.iterator(); while(true){ Foo foo = i.next(); } }catch(NoSuchElementException e){ }
對於iterator這種狀況,返回null是可行的。從性能角度看,返回可識別的值這種方法較好。學習
若是指望調用者可以適當的恢復,對於這種狀況,應該使用受檢異常。this
API設計者讓API用戶面對受檢異常,以強制用戶從這個異常條件中恢復過來,用戶能夠忽視這樣的強制要求,只需捕獲異常並忽略便可,但這每每不是一個好方法。spa
一般用來代表程序錯誤,好比precondition violation,前提違例,即API的客戶沒有遵循API規範創建的約定線程
受檢異常本質上沒有給程序員提供任何好處,反而須要付出努力,使得程序變得複雜。
把受檢異常變爲非受檢異常的一個方法,就是把拋出異常的方法拆分爲兩個,第一個方法返回boolean,代表是否應該拋出異常。例如iterator的hasNext,這種重構並不老是恰當的,可是若是用的合理,API使用起來就更加舒服。固然這種等同於狀態監測的方法,若是線程不安全的話,不推薦這種重構。
A、使得你的API更加易於學習
B、可讀性更好一些
C、異常類越少,則內存印跡越小,裝載這些類的開銷也就越少
A、IllegalArgumentException
傳遞參數不合適的時候
B、IllegalStateException
由於接受對象的狀態而使得調用非法,好比在未初始化完畢以前使用對象
C、ConcurrentModificationException
併發修改
D、UnsupportedOperationException
好比對只支持追加操做的list進行刪除操做
若是方法拋出的異常與它所執行的任務沒有明顯的聯繫,這種情形將會使得人們不知所措,特別是當方法傳遞由低層抽象拋出的異常時,這除了讓人困惑以外,也讓實現細節污染了更高層的API。
爲了不這個問題,須要進行異常轉義(exception transaction),即更高層的實現應該捕獲低層的異常,同時拋出能夠按照高層抽象進行解釋的異常。好比List<E>中的get方法,對低層AbstractSequentialList類拋出的異常進行轉義。
public E get(int index){ ListIterator<E> i = listIterator(index); try{ return i.next(); }catch(NoSuchElementException e){ throw new IndexOutOfBoundsException("Index:"+index); } }
異常鏈是異常轉義的特殊形式,若是低層的異常對於調試致使高層異常的問題很是有幫助的話,使用異常鏈就很合適。大多數標準的異常都有支持鏈的構造器(Throwable cause),對於沒有支持鏈的異常,能夠利用Throwable的initCause方法設置緣由。
儘管異常轉譯與不加選擇地從低層傳遞異常的作法有所改進,可是它也不能被濫用。處理來自低層的異常的最好的作法就是,在調用低層方法以前,先把來自高層的參數校驗成功,從而儘量避免在低層拋出異常。
同時記錄拋出該異常的條件
這樣沒有給調用者任何準確的指導信息
即異常的toString方法,通常包含異常的類名以及異常細節
好比IndexOutOfBoundsException的異常細節應該包含下界、上界以及沒有落在界內的下標值。
能夠定義一個構造器,拋出異常時,傳入這些必要的參數,而後自動產生細節信息。
public IndexOutOfBoundsException(int lowerBound,int upperBound,int index){ super("lower bound:"+lowerBound+ ",upper bound:"+upperBound+ ",index:"+index); this.lowerBound = lowerBound; this.upperBound = upperBound; this.index = index; }
異常細節通常是讓程序員去分析失敗緣由的
即失敗的方法調用應該使對象保持在被調用以前的狀態。
A、設計一個不可變對象
B、對於可變對象,在執行操做以前檢查參數有效性
擴展:儘量調整計算過程的順序,使得可能失敗的部分發生在對象被修改以前
C、編寫一段恢復代碼(recovery code)
不是很經常使用呢,能夠經過攔截處理
D、拷貝對象進行操做,成功以後用其替代對象的內容
好比Collections.sort在執行排序以前,先把輸入列表轉到一個數組中,以便下降在排序的內循環中訪問元素所須要的開銷,另一個考慮就是當排序失敗的時候,也能保證輸入列表保持原樣。
總之,做爲方法規範的一部分,產生的任何異常都應該讓對象保持在該方法調用以前的狀態。
(1)空的catch塊會使異常達不到應有的目的
(2)不重複地記錄異常,能夠在必要的時候調查異常緣由