1. 程式人生 > >《Effective Java》第9章 異常

《Effective Java》第9章 異常

電話 參考資料 輸入 取消 技術 線程停止 調用 個數 表示

第58條:對可恢復的情況使用受檢異常,對編程錯誤使用運行時異常

Java程序設計語言提供了三種可拋出結構(throwable) ;受檢的異常(checked exception)運行時異常(run-time exception)和錯誤(error)。
技術分享
受檢的異常是潛在指示
在決定使用受檢的異常或是未受檢的異常時,主要的原則是:如果期望調用者能夠適當地恢復對於這種情況就應該使用受檢的異常。通過拋出受檢的異常,強迫調用者在一個catch子句中處理該異常。或者將它傳播出去。因此,方法中聲明要拋出的每個受檢的異常,都是
對API用戶的一種潛在指示:與異常相關聯的條件是調用這個方法的一種可能的結果。
IOException、SQLException

用運行時異常來表明編程錯誤
有兩種未受檢的可拋出結構:運行時異常和錯誤。在行為上兩者是等同的:它們都是不需要也不應該被捕獲的可拋出結構。如果程序拋出未受檢的異常或者錯誤,往往就屬於不可恢復的情形,繼續執行下去有害無益。如果程序沒有捕捉到這樣的可拋出結構,將會導致當前線程停止(halt),並出現適當的錯誤消息。

  1. Java.lang.ArithmeticException
  2. Java.lang.ClassCastException
  3. Java.lang.IllegalArgumentException
  4. Java.lang.IndexOutOfBoundsException

雖然JL5 (Java語言規範)並沒有要求,但是按照慣例,錯誤往往被JVM保留用於表示資源不足、約束失敗,或者其他使程序無法繼續執行的條件。由於這己經是個幾乎被普遍接受的慣例,因此最好不要再實現任何新的Error子類。因此,你實現的所有未受檢的拋出結構都
應該是RuntimeException的子類(直接的或者間接的)。

API的設計者往往會忘記,異常也是個完全意義上的對象,可以在它上面定義任意的方法.

因為受檢的異常往往指明了可恢復的條件,所以,對於這樣的異常,提供一些輔助方法尤其重要,通過這些方法,調用者可以獲得些有助於恢復的信息。例如,假設因為用戶沒有儲存足夠數量的錢,他企圖在一個收費電話上進行呼叫就會失敗,於是拋出受檢的異常。這個異常應該提供一個訪問方法,以便允許客戶查詢所缺的費用金額,從而可以將這個數值傳遞給電話用戶。

  • 參考資料
    【1】[Java受檢異常和非受檢異常]http://blog.csdn.net/nlznlz/article/details/53271045
    【2】[Java運行時異常和非運行時異](
    http://blog.csdn.net/huhui_cs/article/details/38817791)

第61條:拋出與抽象相對應的異常

異常轉譯
更高層的實現應該捕獲低層的異常,同時拋出可以按照高層抽象進行解釋的異常。這種做法被稱為異常轉譯(exception translation ),如下所示:
下面的異常轉譯例子取自於AbstractSequentialList類:
技術分享

異常鏈
一種特殊的異常轉譯形式稱為異常鏈(exception chaining),如果低層的異常對於調試導致高層異常的問題非常有幫助,使用異常鏈就很合適。低層的異常(原因)被傳到高層的異常,高層的異常提供訪問方法(Throwable.getCause)來獲得低層的異常:
技術分享

支持鏈
高層異常的構造器將原因傳到支持鏈(chaining-aware)的超級構造器,因此它最終將被傳給Throwable的其中一個運行異常鏈的構造器,例如Throwable (Throwable):
技術分享
大多數標淮的異常都有支持鏈的構造器。對於沒有支持鏈的異常,可以利用Throwable的initCause方法設置原因。異常鏈不僅讓你可以通過程序(用getCause)訪問原因,它還可以將原因的堆棧軌跡集成到更高層的異常中。

第64條:努力使失敗保持原子性

一般而言,失敗的方法調用應該使對象保持在被調用之前的狀態。具有這種屬性的方法被稱為具有失敗原子性(failure atomic ) 。

不可變的對象
有幾種途徑可以實現這種效果。最簡單的辦法莫過於設計一個不可變的對象。

可變對象執行前先檢查
對於在可變對象上執行操作的方法,獲得失敗原子性最常見的辦法是,在執行操作之前檢查參數的有效性(見第38條)。這可以使得在對象的狀態被修改之前,先拋出適當的異常。
技術分享
如果取消對初始大小(size)的檢杳,當這個方法企圖從一個空棧中彈出元素時,它仍然會拋出異常。然而,這將會導致size域保持在不一致的狀態(負數)之中,從而導致將來對該對象的任何方法調用都會失敗.

編寫恢復代碼
第三種獲得失敗原子性的辦法遠遠沒有那麽常用,做法是編寫一段恢復代碼(recovery code),由它來攔截操作過程中發生的失敗,以及使對象回滾到操作開始之前的狀態上。這種辦法主要用於永久性的(基於磁盤的(disk-based))數據結構。

臨時拷貝
最後一種獲得失敗原子性的辦法是,在對象的一份臨時拷貝上執行操作。當操作完成之後再用臨時拷貝中的結果代替對象的內容。
例如,Collections.sort在執行排序之前,首先把它的輸入列表轉到一個數組中,以便降低在排序的內循環中訪問元素所需要的開銷。這是出於性能考慮的做法,但是,它增加了一項優勢:即使排序失敗,它也能保證輸入列表保持原樣。

錯誤(相對於異常)通常是不可恢復的,當方法拋出錯誤時,它們不需要努力保持失敗原子性。

《Effective Java》第9章 異常