使用 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 。)