Java異常的優點與缺陷,及其處理原則

      最近在作一個讀取數據庫元數據的框架,其中的數據庫的檢查異常讓人印象深入。try-catch簡直讓人抓狂,同時做爲框架哪些異常時應該拋出來給調用人員,哪些是應該本身處理掉的,拋出來的異常時檢查異常仍是非檢查異常都值得深思。下面不少僅僅是我的觀點,但願你們補充和指出不對之處。html

 

Java理念:結構不佳的代碼不能運行。(泛型這點作的不好)前端

優勢:一、用強制規定的形式來消除錯誤處理過程當中爲所欲爲的因素;(C語言printf沒人檢查,scanf則會)。java

二、可以下降錯誤處理代碼的複雜度。(不須要太多的if-else)程序員

三、攜帶信息,易於發現問題。(得益於強大的StackTrace棧軌跡)web

 

什麼是異常?spring

在當前環境下沒法獲取必要的信息來解決的,而且會影響當前方法或者做用域繼續執行的問題。前面半句,你無法解決的問題,就交給你上一級來處理,所謂的拋出異常。若是你能解決的,你沒解決,那就是你的程序有問題。下半句,可有可無的異常,你能夠處理掉(要留下信息),log4j找不到properties總不能讓系統直接崩潰,就只這個理。數據庫

Try塊的意義編程

方法內部拋出異常,這個方法就結束了(向更高層拋出)。若是你不但願結束,能夠用try塊。Try就是把可能拋出同樣的方法集中處理。在沒有異常處理的語言中,你可能須要不停的檢查每一個操做是否正確,在Java中能夠集中處理。這對應的就是異常的優勢2。api

異常說明(檢查異常的由來)網絡

Java鼓勵人們把方法可能拋出的異常告知使用此方法的客戶端程序員。

Void f() throws TooBig,TooSmall,DivZero

說明方法f,有且只會拋出上面三個異常(RuntimeException除外)。Java認爲這種種優雅的作法,可是也照成了很大的詬病。IO、數據庫操做的時候,程序中會包含大量的異常處理。若是程序很是大,try-catch-finally簡直就是災難。

         Spring jdbc中的JdbcTemplate把全部的數據庫異常轉換爲DataAccessException異常,轉換爲一個Runtime異常。它進行二次封裝的理由就是不須要用戶關心特定數據庫的細節,不須要強制用戶使用try-catch。它在拋出的異常中,會把原來的信息給保存起來,即所謂的異常鏈。這很是重要,異常鏈對於調試的人員來講很是有用。Hibernate的SchemaExport就是一個糟糕的設計。

         JdbcTemplate還阻止了一些不可能處理異常的拋出,如關閉數據庫鏈接的異常,就算拋出給上層。上層一般也是沒法處理的,還不如catch掉,而後用日誌的形式輸出。

 

 

使用心得或注意方式

一、  須要釋放資源的,必定要放在finally裏面。

  finally塊中的代碼是必定會被執行的。若是一下代碼,在try中就有return,可是結束「照樣」會輸出。因此,當程序中包含數據庫鏈接、IO、網絡鏈接等須要釋放資源的狀況時,必定要使用finnally,避免沒必要要的消耗。

Int f(){

    try{

          return 0;
  }finally{
         System.out.println(「結束」);
  }
}

在hibernate4.0版本之前,它就犯過資源沒有釋放的問題。

 

 

  在 Configuration.buildSessionFactory() 函數中:settings變量包含持有鏈接池的對象。獲取的鏈接對象。若是SessionFactoryImpl建立異常,conn也將得不到釋放。

二、  finally中毫不使用return與throw

finally裏中若是拋出異常或者使用return,本來應該拋出的異常會丟失。以下所示,本意在f()中應該是要拋出Runtime異常的,不過在finnally中有了return就再也不能捕捉到異常了。

static void f(){
      try{

        System.out.println("here");

        throw new RuntimeException("my exception");

      }finally{

        return;

      }

   }
   public static void main(String[] args) {

      f();

   }

 

三、  若是要調用傳入的參數的某個方法,必定要Null檢查。或者該信息必不可少。

  若是不進行null檢查,虛擬機也會幫你自動拋出runtime的NEP錯誤。因此,不少人並不在乎這件事情。若是你的方法會被別人調用,且別人看不到你的方法代碼,那問題就大了。

   static void f(String str){

      System.out.println(str.length());

   }
   public static void main(String[] args) {

      f(null);

}

  如上圖的代碼,拋出的異常信息顯示,f方法中的第9行出現了NPE。可是,極可能別人調用你的代碼時是已經編譯過的class。在這種狀況下,調用者就很難發現問題。因此,必定要進行異常檢查,拋出一個新的並帶有相關的提示信息。開源框架的代碼都會進行這樣的檢查,如jdbcTemplate的某個方法,經過Assert.notNull對action進行了相關檢查,若是爲null拋出runtime異常。

 

四、  檢查異常與非檢查異常

Java的異常結構體系以下所示:

 

  其中的RuntimeException及其子類異常時非檢查異常。在編程時不強制你顯示處理。若是一個異常是致命的且不可恢復而且對於捕獲該異常的方法根本不知如何處理時,或者捕獲這類異常無任何益處,一般應該定義這類異常爲非檢查異常,由頂層專門的異常處理程序處理;像數據庫鏈接錯誤、網絡鏈接錯誤或者文件打不開等之類的異常通常均屬於非檢查異常。這類異常通常與外部環境相關,一旦出現,基本沒法有效地處理。

  Spring Dao選擇了把全部SQLException轉換成了DataAccessException異常,是runtime的。這樣,編程的代結構和可讀性有了巨大的提高。如數據庫關閉異常的,甚至直接catch並記錄相關過程,連異常也不拋出。

  而對於一些具有能夠迴避異常或預料內能夠恢復並存在相應的處理方法的異常,能夠定義該類異常爲檢查異常。像通常由輸入不合法數據引發的異常或者與業務相關的一些異常,基本上屬於檢查異常。當出現這類異常,通常能夠通過有效處理或經過重試能夠恢復正常狀態。

五、  異常視角

 

使用者不該該接觸Java異常,程序不該該在沒有提示的狀況下忽然崩潰。Java像如今應用比較普遍的地方應是web方面。Tomcat之類的web容器可以很好的處理異常,通常狀況內部的邏輯錯誤不會形成應用的崩潰。異常信息也會在前端頁面顯示,在開發階段或者內部測試期間應該直接暴露異常信息至前端,這樣有利於報告錯誤。正式發佈階段,應該將500等錯誤直接跳轉至專門的頁面。

六、  catch異常後沖洗throw異常,應該包含異常鏈

異常鏈對於程序調試尤其關鍵。異常鏈的保留能夠追溯異常源,這樣有利於異常的正確和及時定位。異常鏈的丟失,有的時候絕對是致命的。

 

參考: http://www.ibm.com/developerworks/cn/java/j-lo-exceptionframework/index.html?ca=dat

相關文章
相關標籤/搜索