Java 基礎-異常處理
在 Java 中聲明瞭很多異常類,每個異常類都表示一種執行錯誤。程式執行過程中發生一個可識別的執行錯誤時(可以找到與錯誤匹配的異常類,例如被除數為 0 時會觸發 java.lang.ArithmeticException
),系統會丟擲對應異常類的物件。
參考:Java 異常處理
Java 異常處理機制的優點
- 分離錯誤處理程式碼,使業務程式碼更專注
- 按照型別對錯誤分組
- 可以捕獲處理無法預測的錯誤
- 異常類的物件包含了異常的充分資訊
- 可以按照呼叫棧傳播錯誤,直到有處理錯誤的程式碼
錯誤分類
根據錯誤的嚴重程度不同,可以分為兩類:
- 錯誤:致命的,無法處理的。最上層父類是 Error 類
- 異常:非致命,可以捕獲並處理。最上層父類是 Exception 類
Throwable 是 Error 和 Exception 的父類。
Java 中有3種方式生成異常物件:
- 由 Java 虛擬機器生成
- 由 Java 類庫中的某些類生成
- 在自己寫的程式中生成和丟擲異常物件
必須通過 throw 語句拋異常物件,異常物件必須是 Throwable 或其子類的例項。
throw new XXException();
XXException e = new XXException();
throw e;
Throwable 類的主要方法
方法 | 描述 |
---|---|
public String getMessage() | 獲取異常的詳細資訊 |
public Throwable getCause() | 返回 Throwable 物件,代表異常的原因 |
public void printStackTrace() | 列印toString()結果和棧層次到 System.err 錯誤輸出流。 |
public StackTraceElement [] getStackTrace() | 返回一個包含堆疊層次的陣列。下標為0的元素代表棧頂,最後一個元素代表方法呼叫堆疊的棧底。 |
異常的分類
根據是否必須捕獲異常,可以將異常分為兩類:
非檢查型異常(也叫執行時異常)
非檢查型異常繼承自 RuntimeException,不需要在程式中進行捕獲,編譯器也不會檢查。之所以可以不捕獲,有兩個原因:
- 引發這類異常的操作經常出現(例如使用物件時如果物件為 null 則拋異常)
- 要拋的異常可以用其他方式解決(例如被除數為0會丟擲異常,但可以提前判斷被除數來防止這種異常)
public class Excep {
public static void main(String[] args) {
String[] arr = {"hello", "world"};
int i = 0;
while(i < 10) {
System.out.println(arr[i++]);
}
}
}
上面這段程式碼並沒有捕獲異常,編譯通過,執行時會因為陣列下標越界而報錯:
[[email protected]_139_38_centos java]# java Excep
hello
world
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 2
at Excep.main(Excep.java:6)
檢查型異常
對於檢查型異常,呼叫者必須捕獲並處理(try{...}catch(...){...}
)或也宣告丟擲(`throws XXException``)。如果所有方法都沒有處理這種異常,最終異常會交給 Java 虛擬機器來處理。編譯器會檢查這種異常。
public class T {
public static void main(String[] args) {
He h = new He();
h.fun();
}
}
class He {
public void fun() throws Exception {
throw new RuntimeException();
}
}
上面這段程式碼中,類 He 中的 fun 方法會丟擲異常,當時呼叫者並沒有捕獲也沒有繼續丟擲,所以執行時會報錯:
T.java:4: unreported exception java.lang.Exception; must be caught or declared to be thrown
h.fun();
^
1 error
如果異常一路都在拋,沒有方法處理,最後到了 main 方法還在拋異常public static void main(String[] args) throws Exception{...}
,那最後 Java 虛擬機器會接收到這個異常,程式會停止執行並報錯,例如:
public class T {
public static void main(String[] args) throws Exception{
He h = new He();
h.fun();
System.out.println("end of program");
}
}
class He {
public void fun() throws Exception {
throw new RuntimeException();
}
}
[[email protected]_139_38_centos java]# java T
Exception in thread "main" java.lang.RuntimeException
at He.fun(T.java:10)
at T.main(T.java:4)
捕獲並處理異常
Java 中用 try{...}catch(...){...}finally{...}
語句捕獲異常。其中,try 程式碼段中是可能丟擲異常的程式碼,catch 程式碼段在匹配異常時執行,finally 程式碼段則是無論是否發生異常都會執行的程式碼段。語法為:
try {
// 程式程式碼
} catch(ExceptionName1 e1) {
//Catch 塊
} catch(ExceptionName2 e2) {
//可以有多個 Catch 塊
} finally {
//finally 塊
}
例如,對於陣列越界異常的捕獲處理:
public class ExcepTest{
public static void main(String args[]){
try{
int a[] = new int[2];
System.out.println("Access element three :" + a[3]);
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("Exception thrown :" + e);
}finally{
System.out.println("finally run");
}
System.out.println("Out of the block");
}
}
結果為:
Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3
finally run
Out of the block
throws/throw 關鍵字
對於檢查性異常,如果方法不想捕獲,那麼該方法必須使用 throws 關鍵字來丟擲這個異常(如果有多個可能的異常,需要用逗號分隔),交給呼叫者來處理。throws 關鍵字放在方法簽名的尾部。
public void fun() throws RemoteException, InsufficientFundsException
{
// Method implementation
}
throw 關鍵字用於丟擲異常給方法的呼叫者,注意需要在方法簽名尾部用 throws
丟擲這個異常。
public void fun() throws RemoteException
{
throw new RemoteException();
}
自定義異常
Java 中的異常類跟普通的類一樣,有屬性和方法。根據自定義的異常是否需要檢查,可以分為兩類:
- 檢查型異常類,需要繼承 Exception 類。
- 執行時異常類,需要繼承 RuntimeException 類。
語法示例:
class MyException extends Exception{
}
程式碼示例:
public class T {
public static void main(String[] args) {
try {
throw new MyException(666);
} catch (MyException e) {
System.out.println(e);
}
}
}
class MyException extends Exception {
private int amount;
public MyException(int amount) {
this.amount = amount;
}
public String getMessage() {
return "amount not enough" + this.amount;
}
}
執行結果:
MyException: amount not enough666