Java中的異常處理
一、異常類的層次圖
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException
...
java.lang.Error
java.lang.ThreadDeath
java.lang.VirtualMachineError
...
1、Throwable
Throwable 類是 Java 語言中所有錯誤或異常的超類。只有當對象是此類(或其子類之一)的執行個體時,才能通過 JAVA 虛擬機器或者 Java throw 語句拋出。類似地,只有此類或其子類之一才可以是 catch 子句中的參數類型。
兩個子類的執行個體:Error 和 Exception,通常用於指示發生了異常情況。通常,這些執行個體是在異常情況的上下文中新近建立的,因此包含了相關的資訊(比如堆疊追蹤資料)。
2、Exception
Exception 類及其子類是 Throwable 的一種形式,它指出了合理的應用程式想要捕獲的條件,表示程式本身可以處理的異常。
3、Error
Error 是 Throwable 的子類,表示僅靠程式本身無法恢複的嚴重錯誤,用於指示合理的應用程式不應該試圖捕獲的嚴重問題。
在執行該方法期間,無需在方法中通過throws聲明可能拋出但沒有捕獲的 Error 的任何子類,因為Java編譯器不去檢查它,也就是說,當程式中可能出現這類異常時,即使沒有用try...catch語句捕獲它,也沒有用throws字句聲明拋出它,還是會編譯通過。
4、RuntimeException
RuntimeException是Exception的子類, 是那些可能在 JAVA 虛擬機器正常運行期間拋出的異常的超類。Java編譯器不去檢查它,也就是說,當程式中可能出現這類異常時,即使沒有用try...catch語句捕獲它,也沒有用throws字句聲明拋出它,還是會編譯通過,這種異常可以通過改進代碼實現來避免。
5、ThreadDeath
ThreadDeath是Error的子類,在調用 Thread 類中帶有零參數的 stop 方法時,受害線程將拋出一個 ThreadDeath 執行個體。
僅當應用程式在被非同步終止後必須清除時才應該捕獲這個類的執行個體。如果 ThreadDeath 被一個方法捕獲,那麼將它重新拋出非常重要,因為這樣才能讓該線程真正終止。
二、運行時異常和受檢查異常
Exception類可以分為兩種:運行時異常和受檢查異常。
1、運行時異常
這意味著程式存在bug,如數組越界,0被除,入參不滿足規範.....這類異常需要更改程式來避免。當程式中可能出現這類異常時,即使沒有用try...catch語句捕獲它,也沒有用throws字句聲明拋出它,還是會編譯通過。例如,當除數為零時,就會拋出java.lang.ArithmeticException異常。
2、受檢查異常
程式正確,但因為外在的環境條件不滿足引發。例如:使用者錯誤及I/O問題----程式試圖開啟一個並不存在的遠程Socket連接埠。這不是程式本身的邏輯錯誤,而很可能是遠程機器名字錯誤(使用者拼字錯誤)。對商用軟體系統,程式開發人員必須考慮並處理這個問題。JAVA編譯器強制要求處理這類異常,如果不捕獲這類異常,程式將不能被編譯。
3、兩者的區別
運行時異常表示無法讓程式恢複啟動並執行異常,導致這種異常的原因通常是由於執行了錯誤的操作。一旦出現錯誤,建議讓程式終止。
受檢查異常表示程式可以處理的異常。如果拋出異常的方法本身不處理或者不能處理它,那麼方法的調用者就必須去處理該異常,否則調用會出錯,連編譯也無法通過。當然,這兩種異常都是可以通過程式來捕獲並處理的,比如除數為零的運行時異常:
4、執行階段錯誤
Error類及其子類表示執行階段錯誤,通常是由Java虛擬機器拋出的,JDK中與定義了一些錯誤類,比如VirtualMachineError和OutOfMemoryError,程式本身無法修複這些錯誤.一般不去擴充Error類來建立使用者自訂的錯誤類。而RuntimeException類表示程式碼中的錯誤,是可擴充的,使用者可以建立特定運行時異常類。
Error(執行階段錯誤)和運行時異常的相同之處是:Java編譯器都不去檢查它們,當程式運行時出現它們,都會終止運行。
5、最佳解決方案
對於運行時異常,我們不要用try...catch來捕獲處理,而是在程式開發調試階段,盡量去避免這種異常,一旦發現該異常,正確的做法就會改進程式設計的代碼和實現方式,修改程式中的錯誤,從而避免這種異常。捕獲並處理運行時異常是好的解決辦法,因為可以通過改進代碼實現來避免該種異常的發生。
對於受檢查異常,沒說的,老老實實去按照異常處理的方法去處理,要麼用try...catch捕獲並解決,要麼用throws拋出!
對於Error(執行階段錯誤),不需要在程式中做任何處理,出現問題後,應該在程式在外的地方找問題,然後解決。
三、異常處理的文法規則:
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、如果一個方法調用了另外一個聲明拋出異常的方法,那麼這個方法要麼處理異常,要麼聲明拋出。
6、throw和throws關鍵字的區別:
throw用來拋出一個異常,在方法體內。文法格式為:throw 異常對象。
throws用來聲明方法可能會拋出什麼異常,在方法名後,文法格式為:throws 異常類型1,異常類型2...異常類型n。
四、自訂異常類
建立Exception或者RuntimeException的子類即可得到一個自訂的異常類。例如:
public classMyException
extends Exception{
public MyException(){}
public MyException(String smg){
super(smg);
}
}
五、異常轉型和異常鏈
異常轉型實際上就是捕獲到異常後,將異常以新的類型的異常再拋出,這樣做一般為了異常的資訊更直觀!
異常鏈就是把原始的異常封裝為新的異常類,並在新的異常類中封裝了原始異常類,這樣做的目的在於找到異常的根本原因。
支援異常轉型和異常鏈的Throwable的兩個建構函式:
Throwable(String message, Throwable cause)
Throwable(Throwable cause)
擷取原始異常類的方法:
getCause():返回此 throwable 的 cause;如果 cause 不存在或未知,則返回 null。
例如:
privatevoid Run()throws
MyException {
try{
//...
}catch(IOException e){
//...
thrownewMyException(e);
}finally{
//...
}
}
六、Java異常處理的原則和技巧
1、避免過大的try塊,不要把不會出現異常的代碼放到try塊裡面,盡量保持一個try塊對應一個或多個異常。
2、細化異常的類型,不要不管什麼類型的異常都寫成Excetpion。
3、catch塊盡量保持一個塊捕獲一類異常,不要忽略捕獲的異常,捕獲到後要麼處理,要麼轉譯,要麼重新拋出新類型的異常。
4、不要把自己能處理的異常拋給別人。
5、不要用try...catch參與控製程序流程,異常控制的根本目的是處理常式的非正常情況。