使用 try-catch 捕獲異常

可以使用 try...catch 語句捕獲和處理異常。 (事實上​​,try 語句採用其他形式,如其他關於 try...catch...finallytry-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。由於 RuntimeExceptionException 的子型別,因此第一個(更一般的)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 型別的最不常見的超型別。但是,如果 ecatch 塊中重新丟擲,則丟擲的異常型別是 union 中的型別。例如:

public void method() throws IOException, SQLException
    try {
        doSomething();
    } catch (IOException | SQLException e) {
        report(e);
        throw e;
    }

在上面,IOExceptionSQLException 是檢查的例外,其最不常見的超型別是 Exception。這意味著 report 方法必須匹配 report(Exception)。但是,編譯器知道 throw 只能投擲 IOException 或者 SQLException。因此,method 可以宣告為 throws IOException, SQLException 而不是 throws Exception。 (這是一件好事:看看陷阱 - 投擲 Throwable,Exception,Error 或 RuntimeException 。)