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
異常的分類
1.非執行時異常(Checked Exception)
Java中凡是繼承自Exception但不是繼承自RuntimeException的類都是非執行時異常。
2.執行時異常(Runtime Exception/Unchecked Exception)
RuntimeException類直接繼承自Exception類,稱為執行時異常。Java中所有的執行時異常都直接或間接的繼承自RuntimeException。
二 Java異常處理機制
1 異常處理
1.1 對於可能出現異常的程式碼,有兩種處理辦法:
1.在方法中用try...catch語句捕獲並處理異常,catach語句可以有多個,用來匹配多個異常。例如:
- publicvoid test(){
- try{
- ...
- }catch(IOException e){
- ...
- }catch(Exception e){
- ...
- }finally{
- ...
- }
- }
2.對於處理不了的異常或者要轉型的異常,在方法的宣告處通過throws語句丟擲異常。例如:
- publicvoid test() throws MyException{
- ...
- if(....){
- thrownew MyException();
- }
- }
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 最後還應該注意一下異常處理的語法規則:
- try語句不能單獨存在,可以和catch、finally組成 try...catch...finally、try...catch、try...finally三種結構,catch語句可以有一個或多個,finally語句最多一個,try、catch、finally這三個關鍵字均不能單獨使用。
- try、catch、finally三個程式碼塊中變數的作用域分別獨立而不能相互訪問。如果要在三個塊中都可以訪問,則需要將變數定義到這些塊的外面。
- 多個catch塊時候,Java虛擬機器會匹配其中一個異常類或其子類,就執行這個catch塊,而不會再執行別的catch塊。
- throw語句後不允許有緊跟其他語句,因為這些沒有機會執行。
- 如果一個方法呼叫了另外一個宣告丟擲異常的方法,那麼這個方法要麼處理異常,要麼宣告丟擲。
2.4 throw和throws關鍵字的區別
throw用來丟擲一個異常,在方法體內。語法格式為:throw 異常物件。
throws用來宣告方法可能會丟擲什麼異常,在方法名後,語法格式為:throws 異常型別1,異常型別2...異常型別n。
三 自定義異常
自定義異常通常是定義一個繼承自Exception類的子類。一般情況下我們都會直接繼承自Exception類,而不會繼承某個執行時的異常類。
3.1 自定義受檢查異常(Checked Exception)
1.繼承Exception,並覆蓋構造方法
- publicclass MyException extends Exception{
- public MyException(){
- super();
- }
- public MyException(String msg){
- super(msg);
- }
- }
2.在類中使用自定義異常
- publicclass ExceptionTest {
- publicstaticvoid execute(String a) throws MyException {
- System.out.println("execute...");
- if("true".equals(a)){
- thrownew MyException("引數不能為 true");
- }
- }
- }
3.2 自定義執行時異常
1.繼承自RuntimeException
- publicclass MyRTException extends RuntimeException { //或者繼承任何標準異常類
- /**
- *
- */
- privatestaticfinallong serialVersionUID = 1L;
- public MyRTException(){ //用來建立無引數物件
- }
- public MyRTException(String message) { //用來建立指定引數物件
- super(message); //呼叫超類構造器
- }
- }
2.在類中使用自定義異常
- publicclass ExceptionTest {
- publicstaticvoid execute(String a){
- System.out.println("execute...");
- if("true".equals(a)){
- thrownew MyRTException("引數不能為 true");
- }
- }
- }
四 異常轉型和異常鏈
異常轉型實際上就是捕獲到異常後,將異常以新的型別的異常再丟擲,這樣做一般為了異常的資訊更直觀,比如:
- publicvoid run() throws MyException{
- ...
- try{
- ...
- }catch(IOException e){
- ...
- thrownew MyException();
- }finally{
- ...
- }
- }
異常鏈,在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異常處理的原則和技巧
- 避免過大的try塊,不要把不會出現異常的程式碼放到try塊裡面,儘量保持一個try塊對應一個或多個異常。
- 細化異常的型別,不要不管什麼型別的異常都寫成Excetpion。
- catch塊儘量保持一個塊捕獲一類異常,不要忽略捕獲的異常,捕獲到後要麼處理,要麼轉譯,要麼重新丟擲新型別的異常。
- 不要把自己能處理的異常拋給別人。
- 不要用try...catch參與控制程式流程,異常控制的根本目的是處理程式的非正常情況。