讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 建議117:多使用異常,把性能問題放一邊 >

建議117:多使用異常,把性能問題放一邊

我們知道異常是主邏輯的例外邏輯,舉個簡單例子來說,比如我在馬路上走(這是主邏輯),突然開過一輛車,我要避讓(這是受檢異常,必須處理),繼續走著,突然一架飛機從我頭頂飛過(非受檢異常),我可以選擇繼續行走(不捕捉),也可以選擇指責其噪音污染(捕捉,主邏輯的補充處理),再繼續走著,突然一顆流星砸下來,這沒有選擇,屬於錯誤,不能做任何處理。這樣具備完整例外情景的邏輯就具備了OO的味道,任何一個事物的處理都可能產生非預期結果,問題是需要以何種手段來處理,如果不使用異常就需要依靠返回值的不同來進行處理了,這嚴重失去了面向對象的風格。

我們在編寫用例文檔(Use Case Specification)時,其中有一項叫作「例外事件」,是用來描述主場景外的例外場景的,例如用戶登錄的用例,就會在「例外事件」中說明「連續3次登錄失敗即鎖定用戶賬號」,這就是登錄事件的一個異常處理,具體到我們的程序中就是:


public void login(){

try{

//正常登錄

}catch(InvalidLoginException lie){

//用戶名無效

}catch(InvalidPsswordException pe){

//密碼錯誤的異常

}

}catch(TooMuchLoginException tmle){

//多次登錄失敗的異常

}

}


如此設計則可以讓我們的login方法更符合實際的處理邏輯,同時使主邏輯(正常登錄,try代碼塊)更加清晰。當然了,使用異常還有很多優點,比如可讓正常代碼和異常代碼分離、能快速查找問題(棧信息快照)等,但是異常有一個缺點:性能比較慢。

Java的異常處理機制確實比較慢,這個「比較慢」是相對於諸如String、Integer等對像來說的,單單從對象的創建上來說,new一個IOException會比String慢5倍,這從異常的處理機制上也可以解釋:因為它要執行fillInStackTrace方法,要記錄當前棧的快照,而String類則是直接申請一個內存創建對象,異常類慢一籌也就在所難免了。

而且,異常類是不能緩存的,期望預先建立大量的異常對像以提高異常性能也是不現實的。

難道異常的性能問題就沒有任何可提高的辦法了?確實沒有,但是我們不能因為性能問題而放棄使用異常,而且經過測試,在JDK 1.6下,一個異常對像創建的時間只需要1.4毫秒左右(注意是毫秒,通常一個交易處理是在100毫秒左右),難道我們的系統連如此微小的性能消耗都不允許嗎?

注意 性能問題不是拒絕異常的借口。