異常的高階功能

此示例介紹了一些高階功能和異常用例。

以程式設計方式檢查 callstack

Version >= Java SE 1.4

異常堆疊跟蹤的主要用途是提供有關應用程式錯誤及其上下文的資訊,以便程式設計師可以診斷並修復問題。有時它可以用於其他事情。例如,SecurityManager 類可能需要檢查呼叫堆疊以確定是否應該信任正在進行呼叫的程式碼。

你可以使用異常以程式設計方式檢查呼叫堆疊,如下所示:

    Exception ex = new Exception();   // this captures the call stack
    StackTraceElement[] frames = ex.getStackTrace();
    System.out.println("This method is " + frames[0].getMethodName());
    System.out.println("Called from method " + frames[1].getMethodName());

這有一些重要的警告:

  1. StackTraceElement 中提供的資訊有限。沒有比 printStackTrace 顯示的更多資訊。 (框架中區域性變數的值不可用。)

  2. getStackTrace() 的 javadoc 宣告允許 JVM 遺漏幀:

    在某些情況下,某些虛擬機器可能會從堆疊跟蹤中省略一個或多個堆疊幀。在極端情況下,允許沒有關於此 throwable 的堆疊跟蹤資訊的虛擬機器從此方法返回零長度陣列。

優化異常構造

如其他地方所述,構造異常是相當昂貴的,因為它需要捕獲和記錄有關當前執行緒上所有堆疊幀的資訊。有時,我們知道該資訊永遠不會用於給定的異常; 例如堆疊跟蹤永遠不會被列印出來。在這種情況下,我們可以在自定義異常中使用實現技巧來導致無法捕獲資訊。

Throwable 建構函式呼叫 Throwable.fillInStackTrace() 方法時,捕獲 stacktraces 所需的堆疊幀資訊。這個方法是 public,這意味著子類可以覆蓋它。訣竅是覆蓋從 Throwable 繼承的方法,什麼都不做; 例如

  public class MyException extends Exception {
      // constructors

      @Override 
      public void fillInStackTrace() {
          // do nothing
      }
  }

這種方法的問題在於,覆蓋 fillInStackTrace() 的異常永遠不能捕獲堆疊跟蹤,並且在你需要堆疊跟蹤的情況下無用。

擦除或替換堆疊跟蹤

Version >= Java SE 1.4

在某些情況下,以正常方式建立的異常的堆疊跟蹤包含不正確的資訊或開發人員不希望向使用者顯示的資訊。對於這些場景,Throwable.setStackTrace 可用於替換儲存資訊的 StackTraceElement 物件陣列。

例如,以下內容可用於丟棄異常的堆疊資訊:

 exception.setStackTrace(new StackTraceElement[0]);

抑制異常

Version >= Java SE 7

Java 7 引入了 try-with-resources 構造,以及異常抑制的相關概念。請考慮以下程式碼段:

try (Writer w = new BufferedWriter(new FileWriter(someFilename))) {
    // do stuff
    int temp = 0 / 0;    // throws an ArithmeticException
}

當丟擲異常時,try 會在 w 上呼叫 close(),它會重新整理任何緩衝的輸出,然後關閉 FileWriter。但是如果在沖洗輸出時丟擲 IOException 會發生什麼?

會發生什麼是清除資源時丟擲的任何異常都會被抑制。捕獲異常,並將其新增到主要異常的抑制異常列表中。接下來, try-with-resources 將繼續清理其他資源。最後,將重新引入主要異常。

如果在資源初始化期間丟擲異常,或者 try 塊正常完成,則會出現類似的模式。丟擲的第一個異常成為主要異常,並且抑制了由清理引起的後續異常。

可以通過呼叫 getSuppressedExceptions 從主要異常物件中檢索抑制的異常。