Java異常處理和設計【轉】

Java異常處理和設計html

  在程序設計中,進行異常處理是很是關鍵和重要的一部分。一個程序的異常處理框架的好壞直接影響到整個項目的代碼質量以及後期維護成本和難度。試想一下,若是一個項目從頭至尾沒有考慮過異常處理,當程序出錯從哪裏尋找出錯的根源?可是若是一個項目異常處理設計地過多,又會嚴重影響到代碼質量以及程序的性能。所以,如何高效簡潔地設計異常處理是一門藝術,本文下面先講述Java異常機制最基礎的知識,而後給出在進行Java異常處理設計時的幾個建議。java

  如有不正之處,請多多諒解和指正,不勝感激。程序員

  請尊重做者勞動成果,轉載請標明轉載地址:http://www.cnblogs.com/dolphin0520/p/3769804.html數據庫

  如下是本文的目錄大綱:數組

  一.什麼是異常
  二.Java中如何處理異常
  三.深入理解try,catch,finally,throws,throw五個關鍵字
  四.在類繼承的時候,方法覆蓋時如何進行異常拋出聲明
  五.異常處理和設計的幾個建議網絡

一.什麼是異常                                                                              

  異常的英文單詞是exception,字面翻譯就是「意外、例外」的意思,也就是非正常狀況。事實上,異常本質上是程序上的錯誤,包括程序邏輯錯誤和系統錯誤。好比使用空的引用、數組下標越界、內存溢出錯誤等,這些都是意外的狀況,背離咱們程序自己的意圖。錯誤在咱們編寫程序的過程當中會常常發生,包括編譯期間和運行期間的錯誤,在編譯期間出現的錯誤有編譯器幫助咱們一塊兒修正,然而運行期間的錯誤便不是編譯器力所能及了,而且運行期間的錯誤每每是難以預料的。倘若程序在運行期間出現了錯誤,若是置之不理,程序便會終止或直接致使系統崩潰,顯然這不是咱們但願看到的結果。所以,如何對運行期間出現的錯誤進行處理和補救呢?Java提供了異常機制來進行處理,經過異常機制來處理程序運行期間出現的錯誤。經過異常機制,咱們能夠更好地提高程序的健壯性。框架

  在Java中異常被當作對象來處理,根類是java.lang.Throwable類,在Java中定義了不少異常類(如OutOfMemoryError、NullPointerException、IndexOutOfBoundsException等),這些異常類分爲兩大類:Error和Exception。less

  Error是沒法處理的異常,好比OutOfMemoryError,通常發生這種異常,JVM會選擇終止程序。所以咱們編寫程序時不須要關心這類異常。ssh

  Exception,也就是咱們常常見到的一些異常狀況,好比NullPointerException、IndexOutOfBoundsException,這些異常是咱們能夠處理的異常。jvm

  Exception類的異常包括checked exception和unchecked exception(unchecked exception也稱運行時異常RuntimeException,固然這裏的運行時異常並非前面我所說的運行期間的異常,只是Java中用運行時異常這個術語來表示,Exception類的異常都是在運行期間發生的)。

  unchecked exception(非檢查異常),也稱運行時異常(RuntimeException),好比常見的NullPointerException、IndexOutOfBoundsException。對於運行時異常,java編譯器不要求必須進行異常捕獲處理或者拋出聲明,由程序員自行決定。

  checked exception(檢查異常),也稱非運行時異常(運行時異常之外的異常就是非運行時異常),java編譯器強制程序員必須進行捕獲處理,好比常見的IOExeption和SQLException。對於非運行時異常若是不進行捕獲或者拋出聲明處理,編譯都不會經過。

  在Java中,異常類的結構層次圖以下圖所示:

  

  在Java中,全部異常類的父類是Throwable類,Error類是error類型異常的父類,Exception類是exception類型異常的父類,RuntimeException類是全部運行時異常的父類,RuntimeException之外的而且繼承Exception的類是非運行時異常。

  典型的RuntimeException包括NullPointerException、IndexOutOfBoundsException、IllegalArgumentException等。

  典型的非RuntimeException包括IOException、SQLException等。

二.Java中如何處理異常                                                                 

   在Java中若是須要處理異常,必須先對異常進行捕獲,而後再對異常狀況進行處理。如何對可能發生異常的代碼進行異常捕獲和處理呢?使用try和catch關鍵字便可,以下面一段代碼所示:

try  {
   File file =  new  File( "d:/a.txt" );
   if (!file.exists())
     file.createNewFile();
catch  (IOException e) {
   // TODO: handle exception
}

  被try塊包圍的代碼說明這段代碼可能會發生異常,一旦發生異常,異常便會被catch捕獲到,而後須要在catch塊中進行異常處理。

  這是一種處理異常的方式。在Java中還提供了另外一種異常處理方式即拋出異常,顧名思義,也就是說一旦發生異常,我把這個異常拋出去,讓調用者去進行處理,本身不進行具體的處理,此時須要用到throw和throws關鍵字。 

  下面看一個示例:

public  class  Main {
     public  static  void  main(String[] args) {
         try  {
             createFile();
         catch  (Exception e) {
             // TODO: handle exception
         }
     }
     
     public  static  void  createFile()  throws  IOException{
         File file =  new  File( "d:/a.txt" );
         if (!file.exists())
             file.createNewFile();
     }
}

  這段代碼和上面一段代碼的區別是,在實際的createFile方法中並無捕獲異常,而是用throws關鍵字聲明拋出異常,即告知這個方法的調用者此方法可能會拋出IOException。那麼在main方法中調用createFile方法的時候,採用try...catch塊進行了異常捕獲處理。

  固然還能夠採用throw關鍵字手動來拋出異常對象。下面看一個例子:

public  class  Main {
     public  static  void  main(String[] args) {
         try  {
             int [] data =  new  int []{ 1 , 2 , 3 };
             System.out.println(getDataByIndex(- 1 ,data));
         catch  (Exception e) {
             System.out.println(e.getMessage());
         }
         
     }
     
     public  static  int  getDataByIndex( int  index, int [] data) {
         if (index< 0 ||index>=data.length)
             throw  new  ArrayIndexOutOfBoundsException( "數組下標越界" );
         return  data[index];
     }
}

  而後在catch塊中進行捕獲。

  也就說在Java中進行異常處理的話,對於可能會發生異常的代碼,能夠選擇三種方法來進行異常處理:

  1)對代碼塊用try..catch進行異常捕獲處理;

  2)在 該代碼的方法體外用throws進行拋出聲明,告知此方法的調用者這段代碼可能會出現這些異常,你須要謹慎處理。此時有兩種狀況:

    若是聲明拋出的異常是非運行時異常,此方法的調用者必須顯示地用try..catch塊進行捕獲或者繼續向上層拋出異常。

    若是聲明拋出的異常是運行時異常,此方法的調用者能夠選擇地進行異常捕獲處理。

  3)在代碼塊用throw手動拋出一個異常對象,此時也有兩種狀況,跟2)中的相似:

    若是拋出的異常對象是非運行時異常,此方法的調用者必須顯示地用try..catch塊進行捕獲或者繼續向上層拋出異常。

    若是拋出的異常對象是運行時異常,此方法的調用者能夠選擇地進行異常捕獲處理。

  (若是最終將異常拋給main方法,則至關於交給jvm自動處理,此時jvm會簡單地打印異常信息)

三.深入理解try,catch,finally,throws,throw五個關鍵字                   

   下面咱們來看一下異常機制中五個關鍵字的用法以及須要注意的地方。

1.try,catch,finally

  try關鍵字用來包圍可能會出現異常的邏輯代碼,它單獨沒法使用,必須配合catch或者finally使用。Java編譯器容許的組合使用形式只有如下三種形式:

  try...catch...;       try....finally......;    try....catch...finally...

  固然catch塊能夠有多個,注意try塊只能有一個,finally塊是可選的(可是最多隻能有一個finally塊)。

  三個塊執行的順序爲try—>catch—>finally。

  固然若是沒有發生異常,則catch塊不會執行。可是finally塊不管在什麼狀況下都是會執行的(這點要很是注意,所以部分狀況下,都會將釋放資源的操做放在finally塊中進行)。

  在有多個catch塊的時候,是按照catch塊的前後順序進行匹配的,一旦異常類型被一個catch塊匹配,則不會與後面的catch塊進行匹配。

  在使用try..catch..finally塊的時候,注意千萬不要在finally塊中使用return,由於finally中的return會覆蓋已有的返回值。下面看一個例子:

import  java.io.FileInputStream;
import  java.io.FileNotFoundException;
import  java.io.IOException;
 
 
public  class  Main {
     public  static  void  main(String[] args) {
         String str =  new  Main().openFile();
         System.out.println(str);
         
     }
     
     public  String openFile() {
         try  {
             FileInputStream inputStream =  new  FileInputStream( "d:/a.txt" );
             int  ch = inputStream.read();
             System.out.println( "aaa" );
             return  "step1" ;
         catch  (FileNotFoundException e) {
             System.out.println( "file not found" );
             return  "step2" ;
         } catch  (IOException e) {
             System.out.println( "io exception" );
             return  "step3" ;
         } finally {
             System.out.println( "finally block" );
             //return "finally";
         }
     }
}

  這段程序的輸出結果爲:

  能夠看出,在try塊中發生FileNotFoundException以後,就跳到第一個catch塊,打印"file not found"信息,並將"step2"賦值給返回值,而後執行finally塊,最後將返回值返回。

  從這個例子說明,不管try塊或者catch塊中是否包含return語句,都會執行finally塊。

  若是將這個程序稍微修改一下,將finally塊中的return語句註釋去掉,運行結果是:

  

  最後打印出的是"finally",返回值被從新覆蓋了。

  所以若是方法有返回值,切忌不要再finally中使用return,這樣會使得程序結構變得混亂。

 2.throws和thow關鍵字

  1)throws出如今方法的聲明中,表示該方法可能會拋出的異常,而後交給上層調用它的方法程序處理,容許throws後面跟着多個異常類型;

  2)通常會用於程序出現某種邏輯時程序員主動拋出某種特定類型的異常。throw只會出如今方法體中,當方法在執行過程當中遇到異常狀況時,將異常信息封裝爲異常對象,而後throw出去。throw關鍵字的一個很是重要的做用就是 異常類型的轉換(會在後面闡述道)。

  throws表示出現異常的一種可能性,並不必定會發生這些異常;throw則是拋出了異常,執行throw則必定拋出了某種異常對象。二者都是消極處理異常的方式(這裏的消極並非說這種方式很差),只是拋出或者可能拋出異常,可是不會由方法去處理異常,真正的處理異常由此方法的上層調用處理。

四.在類繼承的時候,方法覆蓋時如何進行異常拋出聲明                        

   本小節討論子類重寫父類方法的時候,如何肯定異常拋出聲明的類型。下面是三點原則:

  1)父類的方法沒有聲明異常,子類在重寫該方法的時候不能聲明異常;

  2)若是父類的方法聲明一個異常exception1,則子類在重寫該方法的時候聲明的異常不能是exception1的父類;

  3)若是父類的方法聲明的異常類型只有非運行時異常(運行時異常),則子類在重寫該方法的時候聲明的異常也只能有非運行時異常(運行時異常),不能含有運行時異常(非運行時異常)。

  

五.異常處理和設計的幾個建議                                                         

   如下是根據前人總結的一些異常處理的建議:

1.只在必要使用異常的地方纔使用異常,不要用異常去控制程序的流程

  謹慎地使用異常,異常捕獲的代價很是高昂,異常使用過多會嚴重影響程序的性能。若是在程序中可以用if語句和Boolean變量來進行邏輯判斷,那麼儘可能減小異常的使用,從而避免沒必要要的異常捕獲和處理。好比下面這段經典的程序:

public  void  useExceptionsForFlowControl() {  
   try  {  
   while  ( true ) {  
     increaseCount();  
     }  
   catch  (MaximumCountReachedException ex) {  
   }  
   //Continue execution  
}  
    
public  void  increaseCount()  throws  MaximumCountReachedException {  
   if  (count >=  5000 )  
     throw  new  MaximumCountReachedException();  
}

  上邊的useExceptionsForFlowControl()用一個無限循環來增長count直到拋出異常,這種作法並無說讓代碼不易讀,而是使得程序執行效率下降。

2.切忌使用空catch塊

  在捕獲了異常以後什麼都不作,至關於忽略了這個異常。千萬不要使用空的catch塊,空的catch塊意味着你在程序中隱藏了錯誤和異常,而且極可能致使程序出現不可控的執行結果。若是你很是確定捕獲到的異常不會以任何方式對程序形成影響,最好用Log日誌將該異常進行記錄,以便往後方便更新和維護。

3.檢查異常和非檢查異常的選擇

  一旦你決定拋出異常,你就要決定拋出什麼異常。這裏面的主要問題就是拋出檢查異常仍是非檢查異常。

  檢查異常致使了太多的try…catch代碼,可能有不少檢查異常對開發人員來講是沒法合理地進行處理的,好比SQLException,而開發人員卻不得不去進行try…catch,這樣就會致使常常出現這樣一種狀況:邏輯代碼只有不多的幾行,而進行異常捕獲和處理的代碼卻有不少行。這樣不只致使邏輯代碼閱讀起來晦澀難懂,並且下降了程序的性能。

  我我的建議儘可能避免檢查異常的使用,若是確實該異常狀況的出現很廣泛,須要提醒調用者注意處理的話,就使用檢查異常;不然使用非檢查異常。

  所以,在通常狀況下,我以爲儘可能將檢查異常轉變爲非檢查異常交給上層處理。

4.注意catch塊的順序

  不要把上層類的異常放在最前面的catch塊。好比下面這段代碼:

try  {
         FileInputStream inputStream =  new  FileInputStream( "d:/a.txt" );
         int  ch = inputStream.read();
         System.out.println( "aaa" );
         return  "step1" ;
     catch  (IOException e) {
        System.out.println( "io exception" );        
          return  "step2" ;
     } catch  (FileNotFoundException e) {
         System.out.println( "file not found" );          
         return  "step3" ;
     } finally {
         System.out.println( "finally block" );
         //return "finally";
     }

  

  第二個catch的FileNotFoundException將永遠不會被捕獲到,由於FileNotFoundException是IOException的子類。

5.不要將提供給用戶看的信息放在異常信息裏

  好比下面這段代碼:

public  class  Main {
     public  static  void  main(String[] args) {
         try  {
             String user =  null ;
             String pwd =  null ;
             login(user,pwd);
         catch  (Exception e) {
             System.out.println(e.getMessage());
         }
         
     }
     
     public  static  void  login(String user,String pwd) {
         if (user== null ||pwd== null )
             throw  new  NullPointerException( "用戶名或者密碼爲空" );
         //...
     }
}

  展現給用戶錯誤提示信息最好不要跟程序混淆一塊兒,比較好的方式是將全部錯誤提示信息放在一個配置文件中統一管理。

6.避免屢次在日誌信息中記錄同一個異常

  只在異常最開始發生的地方進行日誌信息記錄。不少狀況下異常都是層層向上跑出的,若是在每次向上拋出的時候,都Log到日誌系統中,則會致使無從查找異常發生的根源。

7. 異常處理儘可能放在高層進行

  儘可能將異常統一拋給上層調用者,由上層調用者統一之時如何進行處理。若是在每一個出現異常的地方都直接進行處理,會致使程序異常處理流程混亂,不利於後期維護和異常錯誤排查。由上層統一進行處理會使得整個程序的流程清晰易懂。

8. 在finally中釋放資源

  若是有使用文件讀取、網絡操做以及數據庫操做等,記得在finally中釋放資源。這樣不只會使得程序佔用更少的資源,也會避免沒必要要的因爲資源未釋放而發生的異常狀況。                                    

參考資料:http://blessht.iteye.com/blog/908286

     http://www.oschina.net/translate/10-exception-handling-best-practices-in-java-programming

       http://blog.csdn.net/snow_fox_yaya/article/details/1823205

       http://www.iteye.com/topic/72170

       http://www.blogjava.net/gdws/archive/2010/04/25/319342.html

       http://www.2cto.com/kf/201403/284166.html

       http://www.iteye.com/topic/857443

     http://developer.51cto.com/art/200808/85625.htm

     http://www.cnblogs.com/JavaVillage/articles/384483.html

     http://tech.e800.com.cn/articles/2009/79/1247105040929_1.html

     http://blog.csdn.net/zhouyong80/article/details/1907799

     http://blog.csdn.net/luoweifu/article/details/10721543                                  

     《Effective Java中文版》   

做者: 海子
         
本博客中未標明轉載的文章歸做者 海子和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利。
相關文章
相關標籤/搜索