1. 程式人生 > >Java 異常(Exception)&自定義異常

Java 異常(Exception)&自定義異常

Java異常體系結構

java.lang.Object
----java.lang.Throwable
--------java.lang.Exception
------------java.lang.RuntimeException
--------java.lang.Error
------------java.lang.ThreadDeath

Throwable類是 Java 語言中所有錯誤或異常的超類。只有當物件是此類(或其子類之一)的例項時,才能通過 Java 虛擬機器或者 Java throw 語句丟擲。類似地,只有此類或其子類之一才可以是 catch 子句中的引數型別。
它的兩個子類的例項,Error 和 Exception,通常用於指示發生了異常情況。通常,這些例項是在異常情況的上下文中新近建立的,因此包含了相關的資訊(比如堆疊跟蹤資料)。


        Exception 類及其子類是 Throwable 的一種形式,它指出了合理的應用程式想要捕獲的條件,表示程式本身可以處理的異常。

 
        Error 是 Throwable 的子類,表示僅靠程式本身無法恢復的嚴重錯誤,用於指示合理的應用程式不應該試圖捕獲的嚴重問題。
在執行該方法期間,無需在方法中通過throws宣告可能丟擲但沒有捕獲的 Error 的任何子類,因為Java編譯器不去檢查它,也就是說,當程式中可能出現這類異常時,即使沒有用try...catch語句捕獲它,也沒有用throws字句宣告丟擲它,還是會編譯通過。


        RuntimeException

是那些可能在 Java 虛擬機器正常執行期間丟擲的異常的超類。Java編譯器不去檢查它,也就是說,當程式中可能出現這類異常時,即使沒有用try...catch語句捕獲它,也沒有用throws字句宣告丟擲它,還是會編譯通過,這種異常可以通過改進程式碼實現來避免。

異常的分類
1.非執行時異常(Checked Exception)
    Java中凡是繼承自Exception但不是繼承自RuntimeException的類都是非執行時異常。
2.執行時異常(Runtime Exception/Unchecked Exception)
    RuntimeException類直接繼承自Exception類,稱為執行時異常。Java中所有的執行時異常都直接或間接的繼承自RuntimeException。

二 Java異常處理機制

1 異常處理

1.1 對於可能出現異常的程式碼,有兩種處理辦法:
1.在方法中用try...catch語句捕獲並處理異常,catach語句可以有多個,用來匹配多個異常。例如:
 

  1. publicvoid test(){  
  2.     try{  
  3.       ...  
  4.     }catch(IOException e){  
  5.       ...  
  6.     }catch(Exception e){  
  7.       ...  
  8.     }finally{  
  9.       ...  
  10.     }  
  11. }  

2.對於處理不了的異常或者要轉型的異常,在方法的宣告處通過throws語句丟擲異常。例如:
  1. publicvoid test() throws MyException{  
  2.  ...  
  3.  if(....){  
  4.   thrownew MyException();  
  5.  }  
  6. }   


2.Tips:

2.1 如果每個方法都是簡單的丟擲異常,那麼在方法呼叫方法的多層巢狀呼叫中,Java虛擬機器會從出現異常的方法程式碼塊中往回找,直到找到處理該異常的程式碼塊為止。然後將異常交給相應的catch語句處理。如果Java虛擬機器追溯到方法呼叫棧最底部main()方法時,如果仍然沒有找到處理異常的程式碼塊,將按照下面的步驟處理:
        1、呼叫異常的物件的printStackTrace()方法,列印方法呼叫棧的異常資訊。
        2、如果出現異常的執行緒為主執行緒,則整個程式執行終止;如果非主執行緒,則終止該執行緒,其他執行緒繼續執行。
        
        通過分析思考可以看出,越早處理異常消耗的資源和時間越小,產生影響的範圍也越小。因此,不要把自己能處理的異常也拋給呼叫者。

2.2 還有一點,不可忽視:finally語句在任何情況下都必須執行的程式碼,這樣可以保證一些在任何情況下都必須執行程式碼的可靠性。比如,在資料庫查詢異常的時候,應該釋放JDBC連線等等。finally語句先於return語句執行,而不論其先後位置,也不管是否try塊出現異常。finally語句唯一不被執行的情況是方法執行了System.exit()方法。System.exit()的作用是終止當前正在執行的 Java 虛擬機器。finally語句塊中不能通過給變數賦新值來改變return的返回值,也建議不要在finally塊中使用return語句,沒有意義還容易導致錯誤。

2.3 最後還應該注意一下異常處理的語法規則:

  1. try語句不能單獨存在,可以和catch、finally組成 try...catch...finally、try...catch、try...finally三種結構,catch語句可以有一個或多個,finally語句最多一個,try、catch、finally這三個關鍵字均不能單獨使用。
  2. try、catch、finally三個程式碼塊中變數的作用域分別獨立而不能相互訪問。如果要在三個塊中都可以訪問,則需要將變數定義到這些塊的外面。
  3. 多個catch塊時候,Java虛擬機器會匹配其中一個異常類或其子類,就執行這個catch塊,而不會再執行別的catch塊。
  4. throw語句後不允許有緊跟其他語句,因為這些沒有機會執行。
  5. 如果一個方法呼叫了另外一個宣告丟擲異常的方法,那麼這個方法要麼處理異常,要麼宣告丟擲。

2.4 throw和throws關鍵字的區別
        throw用來丟擲一個異常,在方法體內。語法格式為:throw 異常物件。
        throws用來宣告方法可能會丟擲什麼異常,在方法名後,語法格式為:throws 異常型別1,異常型別2...異常型別n。

三 自定義異常
     自定義異常通常是定義一個繼承自Exception類的子類。一般情況下我們都會直接繼承自Exception類,而不會繼承某個執行時的異常類。

3.1 自定義受檢查異常(Checked Exception)

1.繼承Exception,並覆蓋構造方法

  1. publicclass MyException extends Exception{  
  2.     public MyException(){  
  3.         super();  
  4.     }  
  5.     public MyException(String msg){  
  6.         super(msg);  
  7.     }  
  8. }  

2.在類中使用自定義異常
  1. publicclass ExceptionTest {  
  2.     publicstaticvoid execute(String a) throws MyException {  
  3.         System.out.println("execute...");  
  4.         if("true".equals(a)){  
  5.             thrownew MyException("引數不能為 true");  
  6.         }  
  7.     }  
  8. }  

3.2 自定義執行時異常

1.繼承自RuntimeException

  1. publicclass MyRTException extends RuntimeException {    //或者繼承任何標準異常類
  2.     /** 
  3.      *  
  4.      */
  5.     privatestaticfinallong serialVersionUID = 1L;  
  6.     public MyRTException(){ //用來建立無引數物件
  7.     }                  
  8.     public MyRTException(String message) {        //用來建立指定引數物件
  9.         super(message);                             //呼叫超類構造器
  10.     }  
  11. }  

2.在類中使用自定義異常

  1. publicclass ExceptionTest {  
  2.     publicstaticvoid execute(String a){  
  3.         System.out.println("execute...");  
  4.         if("true".equals(a)){  
  5.             thrownew MyRTException("引數不能為 true");  
  6.         }  
  7.     }  
  8. }  
注意:RuntimeException 是那些可能在 Java 虛擬機器正常執行期間丟擲的異常的超類。 RuntimeException 的任何子類都無需在 throws 子句中進行宣告。

四 異常轉型和異常鏈
     異常轉型實際上就是捕獲到異常後,將異常以新的型別的異常再丟擲,這樣做一般為了異常的資訊更直觀,比如:

  1. publicvoid run() throws MyException{  
  2.  ...  
  3.  try{  
  4.   ...  
  5.  }catch(IOException e){  
  6.   ...  
  7.   thrownew MyException();  
  8.  }finally{  
  9.   ...  
  10.  }  
  11. }  


        異常鏈,在JDK1.4以後版本中,Throwable類支援異常鏈機制。Throwable 包含了其執行緒建立時執行緒執行堆疊的快照。它還包含了給出有關錯誤更多資訊的訊息字串。最後,它還可以包含 cause(原因):另一個導致此 throwable 丟擲的 throwable。它也稱為異常鏈 設施,因為 cause 自身也會有 cause,依此類推,就形成了異常鏈,每個異常都是由另一個異常引起的。 
通俗的說,異常鏈就是把原始的異常包裝為新的異常類,並在新的異常類中封裝了原始異常類,這樣做的目的在於找到異常的根本原因。
通過Throwable的兩個構造方法可以建立自定義的包含異常原因的異常型別:

Throwable(String message, Throwable cause) 
          構造一個帶指定詳細訊息和 cause 的新 throwable。 
Throwable(Throwable cause) 
          構造一個帶指定 cause 和 (cause==null ? null :cause.toString())(它通常包含類和 cause 的詳細訊息)的詳細訊息的新 throwable。 


getCause()  方法返回此 throwable 的 cause;如果 cause 不存在或未知,則返回 null。
initCause(Throwable cause)   將此 throwable 的 cause 初始化為指定值。

      在Throwable的子類Exception中,也有類似的指定異常原因的構造方法:
Exception(String message, Throwable cause) 
          構造帶指定詳細訊息和原因的新異常。 
Exception(Throwable cause) 
          根據指定的原因和 (cause==null ? null : cause.toString()) 的詳細訊息構造新異常(它通常包含 cause 的類和詳細訊息)。 

     因此,可以通過擴充套件Exception類來構造帶有異常原因的新的異常類。

五 Java異常處理的原則和技巧

  1. 避免過大的try塊,不要把不會出現異常的程式碼放到try塊裡面,儘量保持一個try塊對應一個或多個異常。
  2. 細化異常的型別,不要不管什麼型別的異常都寫成Excetpion。
  3. catch塊儘量保持一個塊捕獲一類異常,不要忽略捕獲的異常,捕獲到後要麼處理,要麼轉譯,要麼重新丟擲新型別的異常。
  4. 不要把自己能處理的異常拋給別人。
  5. 不要用try...catch參與控制程式流程,異常控制的根本目的是處理程式的非正常情況。