java的checked exception和unchecked exception (轉來的,但確實經典)

異常的概念 
       任何的異常都是Throwable類,並且在它之下包含兩個子類Error / Exception,而Error僅在當在Java虛擬機中發生動態連接失敗或其它的定位失敗的時候,Java虛擬機拋出一個Error對象。典型的程序不捕獲或拋出Errors對象,你可能永遠不會遇到需要實例化Error的應用,那就讓我們關心一下Exception。

       Exception中比較重要的就是RuntimeException(運行時異常)-可能在執行方法期間拋出但未被捕獲的 RuntimeException 的任何子類都無需在 throws 子句中進行聲明,也就是說你的應用應該不去“關心”(說不關心是不服責任的,但只是你不應該試圖實例化它的字類)。  RuntimeException,就如同你不應該關心Error的產生與處理一樣!RuntimeException描述的是程序的錯誤引起來的,因該由程序負擔這個責任!(從責任這個角度看Error屬於JVM需要負擔的責任;RuntimeException是程序應該負擔的責任;checked exception 是具體應用負擔的責任)

       除了Error與RuntimeException,其他剩下的異常都是你需要關心的,而這些異常類統稱爲Checked Exception,至於Error與RuntimeException則被統稱爲Unchecked Exception.

 

         關於 Java 中引入的 Checked Exceptions,目前存在着很多反對意見。正方的觀點是引入 Checked Exceptions,可以增加程度的魯棒性。反方的觀點是 Checked Exceptions 很少被開發人員正確使用過,並且降低了程序開發的生產率和代碼的執行效率。


Java 中定義了兩類異常:

1) Checked exception: 這類異常都是Exception的子類 。異常的向上拋出機制進行處理,如果子類可能產生A異常,那麼在父類中也必須throws A異常。可能導致的問題:代碼效率低,耦合度過高。C#中就沒有使用這種異常機制。

2) Unchecked exception: 這類異常都是RuntimeException的子類,雖然RuntimeException同樣也是Exception的子類,但是它們是特殊的,它們不能通過client code來試圖解決,所以稱爲Unchecked exception 。 

  

(JAVA視線論壇robbin's view,個人覺得用來做業務流程控制違背了Exception設計的初衷,但可以借鑑一下)

     在使用UseCase來描述一個場景的時候,有一個主事件流和n個異常流。異常流可能發生在主事件流的過程,而try語句裏面實現的是主事件流,而catch裏面實現的是異常流,在這裏Exception不代表程序出現了異常或者錯誤,Exception只是面向對象化的業務邏輯控制方法。如果沒有明白這一點,那麼我認爲並沒有真正明白應該怎麼使用Java來正確的編程。
      而我自己寫的程序,會自定義大量的Exception類,所有這些Exception類都不意味着程序出現了異常或者錯誤,只是代表非主事件流的發生的,用來進行那些分支流程的流程控制的。例如你往權限系統中增加一個用戶,應該定義1個異常類,UserExistedException,拋出這個異常不代表你插入動作失敗,只說明你碰到一個分支流程,留待後面的catch中來處理這個分支流程。傳統的程序員會寫一個if else來處理,而一個合格的OOP程序員應該有意識的使用try catch 方式來區分主事件流和n個分支流程的處理,通過try catch,而不是if else來從代碼上把不同的事件流隔離開來進行分別的代碼撰寫。


(另外一種觀點,不同於robbin,個人贊同,並引用於此)

1。什麼時候拋出異常--涉及到服務類
2。拋出checked還是unchecked的異常--涉及到客戶類

       對第一個問題來說,我想異常本身這個字解釋了某些東西,異常就是我們認爲在正常情況下不可能發生的問題,並且服務代碼不知道如何去處理。譬如說我做一個監控程序,需要用壓縮卡提供的API去初始化所有的板卡,API提供的是boolean型的返回值,但我把這個API變成拋出一個異常,因爲除非特殊原因,我不認爲會發生初始化失敗的情況,當然更不知道怎樣去處理這個問題。又譬如Hibernate裏面的LoadObject使用沒有發現這個對象存在,那Hibernate也是認爲不可能的,除非其他代碼直接刪除了數據庫裏面的記錄,那麼也需要拋出異常。當然Hibernate本身也不知道如何處理這種情況。
       但是如果發生的情況是可以預期的,那我不認爲應該拋出例外。象上面這個userExist的情況,我認爲應該在前面已經分流,應該首先判斷這個用戶是否存在,if(userExists()),然後進行處理,而不應當拋出例外。以及login應當返回true或者false。也就是說,這些屬於程序的正常流程,而不是例外,不是異常。把例外作爲正常程序流程的控制機制,只不過是把服務代碼中的if轉移到客戶代碼去,沒有減少任何需要處理的代碼,反而增加了系統的負擔(生成例外棧)。
       還有拋出異常的情況是違反方法的先決條件,每一個方法都有自己的先決條件和後置條件,方法只有在正確的前提下才能執行達到一個正確的後果,(所謂類的不變量)。譬如你去存取一個數組的某一個元素,這個存取方法有一個前提條件,就是你的索引應當落入它的最大下標和最小下標之間,不然就應當拋出一個例外。

         對於第二個問題,端視於客戶代碼是否能夠根據這個例外進行合理的處理。如果客戶代碼根本就不知道如何處理這個例外,應當把它作爲一個unchecked例外,例如上面下標的問題,客戶代碼用一個不合法的下標來存取數組,那麼拋出一個checked例外以後,客戶代碼是+1還是-1?顯然根本就不可能做出“合理的”處理,客戶既然不能處理,還要強制它去處理,那麼就是捕獲,打印了事,沒有增加任何價值。但是如果是客戶可以處理的,或者可以選擇不同的方式處理的,那麼就可能需要用checked,但我發現很少有這樣的情況。對於類似於RemoteException或者SQLException這些Exception,我一般都轉換爲具體的業務Exception,而我所有的業務Exception都是RuntimeException.

         所以我的觀點是,是否拋出例外就是服務代碼是否進行合理的處理,拋出什麼類型的例外就是客戶代碼是否能夠合理的處理。


保護封裝性的代碼實例:

      不要讓你要拋出的checked exception升級到較高的層次。例如,不要讓SQLException延伸到業務層。業務層並不需要(不關心?)SQLException。你有兩種方法來解決這種問題:

 1)轉變SQLException爲另外一個checked exception,如果客戶端並不需要恢復這種異常的話;
 2)轉變SQLException爲一個unchecked exception,如果客戶端對這種異常無能爲力的話;

      多數情況下,客戶端代碼都是對SQLException無能爲力的,因此你要毫不猶豫的把它轉變爲一個unchecked exception,看看下邊的代碼:

public void dataAccessCode(){
try{
      .  .some code that throws SQLException
}catch(SQLException ex){
     ex.printStacktrace();
}
}

       上邊的catch塊緊緊打印異常信息而沒有任何的直接操作,這是情有可原的,因爲對於SQLException你還奢望客戶端做些什麼呢?(但是顯然這種就象什麼事情都沒發生一樣的做法是不可取的)那麼有沒有另外一種更加可行的方法呢? 看下面代碼:
public void dataAccessCode(){
    try{
          ..some code that throws SQLException
      }catch(SQLException ex){
         throw new RuntimeException(ex);
   }
}
     上邊的做法是把SQLException轉換爲RuntimeException,一旦SQLException被拋出,那麼程序將拋出RuntimeException,此時程序被掛起並返回客戶端異常信息。 調用該方法的程序代碼中也不用再捕獲SQLException異常:

public void do(){

     dataAccessCode();   
}

 

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/zhiweiv/archive/2008/12/01/3419952.aspx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章