使用 try-catch 捕獲異常
可以使用 try...catch 語句捕獲和處理異常。 (事實上,try 語句採用其他形式,如其他關於 try...catch...finally 和 try-with-resources 的例子所述 。)
嘗試捕獲一個捕獲塊
最簡單的表單如下所示:
try {
doSomething();
} catch (SomeException e) {
handle(e);
}
// next statement
一個簡單的 try...catch 的行為如下:
- 執行
try塊中的語句。 - 如果
try塊中的語句沒有丟擲異常,則控制轉到try...catch之後的下一個語句。 - 如果在
try塊中丟擲異常。- 測試異常物件以檢視它是
SomeException的例項還是子型別。 - 如果是,那麼
catch塊將捕獲異常:- 變數
e繫結到異常物件。 - 執行
catch塊中的程式碼。 - 如果該程式碼丟擲異常,則傳播新丟擲的異常代替原始異常。
- 否則,控制傳遞到
try...catch之後的下一個語句。
- 變數
- 如果不是,則原始異常繼續傳播。
- 測試異常物件以檢視它是
嘗試捕獲多個捕獲
一個 try...catch 也可以有多個 catch 塊。例如:
try {
doSomething();
} catch (SomeException e) {
handleOneWay(e)
} catch (SomeOtherException e) {
handleAnotherWay(e);
}
// next statement
如果有多個 catch 塊,則從第一個塊開始一次嘗試一個,直到找到異常的匹配。執行相應的處理程式(如上所述),然後將控制權傳遞給 try...catch 語句後的下一個語句。即使處理程式程式碼丟擲異常,也總是跳過匹配的 catch 塊之後的塊。
自上而下匹配策略會對 catch 塊中的異常不是不相交的情況產生影響。例如:
try {
throw new RuntimeException("test");
} catch (Exception e) {
System.out.println("Exception");
} catch (RuntimeException e) {
System.out.println("RuntimeException");
}
此程式碼段將輸出 Exception 而不是 RuntimeException。由於 RuntimeException 是 Exception 的子型別,因此第一個(更一般的)catch 將匹配。第二個(更具體的)catch 永遠不會被執行。
從中學到的教訓是,最具體的 catch 塊(就異常型別而言)應該首先出現,而最常見的塊應該是最後一塊。 (如果永遠不能執行 catch,某些 Java 編譯器會發出警告,但這不是編譯錯誤。)
多異常捕獲塊
Version >= Java SE 7
從 Java SE 7 開始,單個 catch 塊可以處理不相關的異常列表。列出了異常型別,使用豎線(|)符號分隔。例如:
try {
doSomething();
} catch (SomeException | SomeOtherException e) {
handleSomeException(e);
}
多異常捕獲的行為是單異常情況的簡單擴充套件。如果丟擲的異常與(至少)列出的一個異常匹配,則 catch 匹配。
規範中還有一些額外的細微之處。e 的型別是列表中異常型別的合成聯合。當使用 e 的值時,其靜態型別是 union 型別的最不常見的超型別。但是,如果 e 在 catch 塊中重新丟擲,則丟擲的異常型別是 union 中的型別。例如:
public void method() throws IOException, SQLException
try {
doSomething();
} catch (IOException | SQLException e) {
report(e);
throw e;
}
在上面,IOException 和 SQLException 是檢查的例外,其最不常見的超型別是 Exception。這意味著 report 方法必須匹配 report(Exception)。但是,編譯器知道 throw 只能投擲 IOException 或者 SQLException。因此,method 可以宣告為 throws IOException, SQLException 而不是 throws Exception。 (這是一件好事:看看陷阱 - 投擲 Throwable,Exception,Error 或 RuntimeException 。)