在某些情況下,我們想重新擲出剛才產生過的違例,特別是在用Exception捕獲所有可能的違例時。由於我們已擁有當前違例的句柄,所以只需簡單地重新擲出那個句柄即可。下面是一個例子:
catch(Exception e) {
System.out.println("一個違例已經產生");
throw e;
}
重新“擲”出一個違例導致違例進入更高一級環境的違例控制器中。用於同一個try塊的任何更進一步的catch從句仍然會被忽略。此外,與違例對象有關的所有東西都會得到保留,所以用於捕獲特定違例類型的更高一級的控制器可以從那個對象裡提取出所有信息。
若只是簡單地重新擲出當前違例,我們打印出來的、與printStackTrace()內的那個違例有關的信息會與違例的起源地對應,而不是與重新擲出它的地點對應。若想安裝新的堆棧跟蹤信息,可調用fillInStackTrace(),它會返回一個特殊的違例對象。這個違例的創建過程如下:將當前堆棧的信息填充到原來的違例對象裡。下面列出它的形式:
//: Rethrowing.java // Demonstrating fillInStackTrace() public class Rethrowing { public static void f() throws Exception { System.out.println( "originating the exception in f()"); throw new Exception("thrown from f()"); } public static void g() throws Throwable { try { f(); } catch(Exception e) { System.out.println( "Inside g(), e.printStackTrace()"); e.printStackTrace(); throw e; // 17 // throw e.fillInStackTrace(); // 18 } } public static void main(String[] args) throws Throwable { try { g(); } catch(Exception e) { System.out.println( "Caught in main, e.printStackTrace()"); e.printStackTrace(); } } } ///:~
其中最重要的行號在注釋內標記出來。注意第17行沒有設為注釋行。它的輸出結果如下:
originating the exception in f() Inside g(), e.printStackTrace() java.lang.Exception: thrown from f() at Rethrowing.f(Rethrowing.java:8) at Rethrowing.g(Rethrowing.java:12) at Rethrowing.main(Rethrowing.java:24) Caught in main, e.printStackTrace() java.lang.Exception: thrown from f() at Rethrowing.f(Rethrowing.java:8) at Rethrowing.g(Rethrowing.java:12) at Rethrowing.main(Rethrowing.java:24)
因此,違例堆棧路徑無論如何都會記住它的真正起點,無論自己被重復“擲”了好幾次。
若將第17行標注(變成注釋行),而撤消對第18行的標注,就會換用fillInStackTrace(),結果如下:
originating the exception in f() Inside g(), e.printStackTrace() java.lang.Exception: thrown from f() at Rethrowing.f(Rethrowing.java:8) at Rethrowing.g(Rethrowing.java:12) at Rethrowing.main(Rethrowing.java:24) Caught in main, e.printStackTrace() java.lang.Exception: thrown from f() at Rethrowing.g(Rethrowing.java:18) at Rethrowing.main(Rethrowing.java:24)
由於使用的是fillInStackTrace(),第18行成為違例的新起點。
針對g()和main(),Throwable類必須在違例規格中出現,因為fillInStackTrace()會生成一個Throwable對象的句柄。由於Throwable是Exception的一個基礎類,所以有可能獲得一個能夠“擲”出的對象(具有Throwable屬性),但卻並非一個Exception(違例)。因此,在main()中用於Exception的句柄可能丟失自己的目標。為保證所有東西均井然有序,編譯器強制Throwable使用一個違例規范。舉個例子來說,下述程序的違例便不會在main()中被捕獲到:
//: ThrowOut.java public class ThrowOut { public static void main(String[] args) throws Throwable { try { throw new Throwable(); } catch(Exception e) { System.out.println("Caught in main()"); } } } ///:~
也有可能從一個已經捕獲的違例重新“擲”出一個不同的違例。但假如這樣做,會得到與使用fillInStackTrace()類似的效果:與違例起源地有關的信息會全部丟失,我們留下的是與新的throw有關的信息。如下所示:
//: RethrowNew.java // Rethrow a different object from the one that // was caught public class RethrowNew { public static void f() throws Exception { System.out.println( "originating the exception in f()"); throw new Exception("thrown from f()"); } public static void main(String[] args) { try { f(); } catch(Exception e) { System.out.println( "Caught in main, e.printStackTrace()"); e.printStackTrace(); throw new NullPointerException("from main"); } } } ///:~
輸出如下:
originating the exception in f() Caught in main, e.printStackTrace() java.lang.Exception: thrown from f() at RethrowNew.f(RethrowNew.java:8) at RethrowNew.main(RethrowNew.java:13) java.lang.NullPointerException: from main at RethrowNew.main(RethrowNew.java:18)
最後一個違例只知道自己來自main(),而非來自f()。注意Throwable在任何違例規范中都不是必需的。
永遠不必關心如何清除前一個違例,或者與之有關的其他任何違例。它們都屬於用new創建的、以內存堆為基礎的對象,所以垃圾收集器會自動將其清除。