Java 所處理的例外主要可分為兩大類:一種是嚴重的錯誤,例如硬體錯誤或記憶體不足等問題,與此相關的類別是位於 java.lang 下的 Error 類別及其子類別,對於這類的錯誤通常程式是無力自行回復;另一種是非嚴重的錯誤,代表可以處理的狀況,例如使用者輸入了不合格式的資料,這種錯誤程式有機會回復至正常運作狀況,與這類錯誤相關的類別是位於 java.lang 下的 Exception 類別及其子類別。
Error 類別與 Exception 類別都繼承自 Throwable 類別,Throwable 類別擁有幾個取得相關例外訊息的方法。
getLocalizedMessage()
取得例外物件的區域化訊息描述
getMessage()
取得例外物件的訊息描述
printStackTrace()
顯示例外的堆疊訊息,這個方法在追蹤例外發生的根源時相當的有用,簡單的說若 A 方法中呼叫了 B 方法,而 B 方法中呼叫了 C 方法,C 方法產生了例外,則在處理這個例外時呼叫 printStackTrace() 可以得知整個方法呼叫的過程,由此得知例外是如何被層層丟出的。
除了使用這些方法之外,您也可以簡單的利用例外物件 toString() 方法取得例外的簡單訊息描述。
您所接觸的例外通常都是衍生自 Exception 類別,其中是有些「受檢例外」(Checked exception),例如 ClassNotFoundException(嘗試載入類別時失敗所引發,例如類別檔案不存在)、InterruptedException(執行緒非執行中而嘗試中斷所引發的例外)等,而有些是「執行時期例外」(Runtime exception),也稱「非受檢例外」(Unckecked exception),例如 ArithmeticException、ArrayIndexOutOfBoundsException 等。以下列出一些重要的例外繼承架構:
Throwable
Error(嚴重的系統錯誤)
LinkageError
ThreadDeath
VirtualMachineError
....
Exception
ClassNotFoundException
CloneNotSupportedException
IllegalAccessException
....
RuntimeException(執行時期例外)
ArithmeticException
ArrayStoreException
ClassCastException
....
Exception 下非 RuntimeException 衍生之例外類別如果有引發的可能,則您一定要在程式中明確的指定處理才可以通過編譯,因為這些例外是可預期的,編譯器會要求您明確處理,所以才稱之為「受檢例外」(Checked exception),例如當您使用到 BufferedReader 的 readLine() 時,由於有可能引發 IOException 這個受檢例外,您要不就在 try...catch 中處理,要不就在方法上使用 "throws" 表示由呼叫它的呼叫者來處理。
屬於 RuntimeException 衍生出來的類別是「執行時期例外」(Runtime exception),是在執行時期會發生的例外,這個例外不預期它一定會發生,端看程式邏輯寫的如何,因而不需要特別使用 try...catch 或是在方法上使用 "throws" 宣告也可以通過編譯,所以才稱之為「非受檢例外」(Unchecked exception),例如您在使用陣列時,並不一定要處理 ArrayIndexOutOfBoundsException 例外,因為只要程式邏輯寫的正確,這個例外就不會發生。
瞭解例外處理的繼承架構是必要的,例如在捕捉例外物件時,如果父類別例外物件在子類別例外物件之前被捕捉,則 "catch" 子類別例外物件的區塊將永遠不會被執行,事實上編譯器也會幫您檢查這個錯誤,例如:
import java.io.*;
public class ExceptionDemo {
public static void main(String[] args) {
try {
throw new ArithmeticException("例外測試");
}
catch(Exception e) {
System.out.println(e.toString());
}
catch(ArithmeticException e) {
System.out.println(e.toString());
}
}
}
因為 Exception 是 ArithmeticException 的父類別,所以 ArithmeticException 的實例會先被 Exception 的 "catch" 區塊捕捉到,範例 10.7 在編譯時將會產生以下的錯誤訊息:
ExceptionDemo.java:11: exception java.lang.ArithmeticException has already been caught
catch(ArithmeticException e) {
^
1 error
要完成這個程式的編譯,您必須更改例外物件捕捉的順序,如範例 10.8 所示。
import java.io.*;
public class ExceptionDemo2 {
public static void main(String[] args) {
try {
throw new ArithmeticException("例外測試");
}
catch(ArithmeticException e) {
System.out.println(e.toString());
}
catch(Exception e) {
System.out.println(e.toString());
}
}
}
在撰寫程式時,您也可以如範例 10.8 將 Exception 例外物件的捕捉撰寫在最後,以便捕捉到所有您所尚未考慮到的例外,在除蟲(Debug)階段時是很有用的。
如果您要自訂自已的例外類別,您可以繼承 Exception 類別而不是 Error 類別,Error 是屬於嚴重的系統錯誤,程式通常無力從這類的錯誤中回復,所以您不用去處理它,事實上在 Java 程式中也不希望您處理 Error 類別的例外。
如果使用繼承時,父類別的某個方法上宣告了 throws 某些例外,而在子類別中重新定義該方法時,您可以:
但是您不可以: