有時候希望把剛捕獲的異常重新拋出,尤其是在使用Exception捕獲所有異常的時候。既然已經得到了對當前異常對象的引用,可以直接把它重新拋出:
catch(Exception e){
System.out.println("an exception was thrown");
throw e;
}
重新拋出異常會把異常拋給上一級環境中的異常處理程序,同一個try塊的后續catch子句將會被忽略。此外,異常對象的所有信息都得以保持,所以高一級環境中捕獲此異常的處理程序可以從這個異常對象中得到所有信息。
如果只是把當前異常對象重新拋出,那么printStackTrace()方法顯示的將是原來異常拋出的調用棧信息,而非重新拋出點的信息。要向更新這個信息,可以調用filStackTrace()方法,這將返回一個Throwable對象,它是通過把當前調用棧信息填入原來那個異常對象而建立的,就像這樣:
public class Rethrowing {
public static void f() throws Exception {
System.out.println("a_mark");
throw new Exception("b_mark");
}
public static void g() throws Exception {
try {
f();
} catch (Exception e) {
System.out.println("c_mark");
e.printStackTrace(System.out);
throw e;
}
}
public static void h() throws Exception {
try {
f();
} catch (Exception e) {
System.out.println("d_mark");
e.printStackTrace(System.out);
throw (Exception) e.fillInStackTrace();
}
}
public static void main(String[] args) {
try {
g();
} catch (Exception e) {
System.out.println("e_mark");
e.printStackTrace(System.out);
}
System.out.println("\n-------------between line--------------------\n");
try {
h();
} catch (Exception e) {
System.out.println("f_mark");
e.printStackTrace(System.out);
}
}
}
/*輸出如下:
a_mark
c_mark
java.lang.Exception: b_mark
at testThread.Rethrowing.f(Rethrowing.java:7)
at testThread.Rethrowing.g(Rethrowing.java:12)
at testThread.Rethrowing.main(Rethrowing.java:32)
e_mark
java.lang.Exception: b_mark
at testThread.Rethrowing.f(Rethrowing.java:7)
at testThread.Rethrowing.g(Rethrowing.java:12)
at testThread.Rethrowing.main(Rethrowing.java:32)
-------------between line--------------------
a_mark
d_mark
java.lang.Exception: b_mark
at testThread.Rethrowing.f(Rethrowing.java:7)
at testThread.Rethrowing.h(Rethrowing.java:22)
at testThread.Rethrowing.main(Rethrowing.java:41)
f_mark
java.lang.Exception: b_mark
at testThread.Rethrowing.h(Rethrowing.java:26)
at testThread.Rethrowing.main(Rethrowing.java:41)
*/
調用filStackTrace()的那一行就成了異常的重新發生地了。
有可能在捕獲異常之后拋出另一種異常。這么做的話,得到的效果類似於使用filStackTrace(),有關原來異常發生的信息會丟失,剩下的是與新的拋出點有關的信息:
class OneException extends Exception {
public OneException(String s) {
super(s);
}
}
class TwoException extends Exception {
public TwoException(String s) {
super(s);
}
}
public class RethrowNew {
public static void f() throws OneException {
System.out.println("a_mark");
throw new OneException("thrown from f()");
}
public static void main(String[] args) {
try {
try {
f();
} catch (Exception e) {
System.out.println("b_mark");
e.printStackTrace(System.out);
throw new TwoException("from inner try");
}
} catch (Exception e) {
System.out.println("c_mark");
e.printStackTrace(System.out);
}
}
}
最后那個異常僅知道自己來自 main(),而對 f() 一無所知。
永遠不必為清理前一個異常對象而擔心,或者說為異常對象的清理而擔心。他們都是用 new 在堆上創建的對象,所以gc會自動把他們清理掉。
備:throw 與 throws 之區分
throw 是語句拋出一個異常,該語句用在方法體內,表示拋出異常,由方法體內的語句處理。
throws 是方法可能拋出異常的聲明,該語句用在方法聲明后面,表示再拋出異常,由該方法的調用者來處理。
////end