Sometimes you’ll want to rethrow the exception that you just caught, particularly when you use Exception to catch any exception. Since you already have the handle to the current exception, you can simply re-throw that handle:
-
catch(Exception e) {
-
System.out.println("An exception was thrown");
-
throw e;
-
}
-
Rethrowing an exception causes the exception to go to the exception handlers in the next-higher context. Any further catch clauses for the same try block are still ignored. In addition, everything about the exception object is preserved, so the handler at the higher context that catches the specific exception type can extract all the information from that object.
If you simply re-throw the current exception, the information that you print about that exception in printStackTrace( ) will pertain to the exception’s origin, not the place where you re-throw it. If you want to install new stack trace information, you can do so by calling fillInStackTrace( ), which returns an exception object that it creates by stuffing the current stack information into the old exception object. Here’s what it looks like:
-
//: 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();
-
}
-
}
-
} ///:~
-
The important line numbers are marked inside of comments. With line 17 un-commented (as shown), the output is:
-
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)
-
So the exception stack trace always remembers its true point of origin, no matter how many times it gets rethrown.
-
With line 17 commented and line 18 un-commented, fillInStackTrace( ) is used instead, and the result is:
-
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)
-
Because of fillInStackTrace( ), line 18 becomes the new point of origin of the exception.
The class Throwable must appear in the exception specification for g( ) and main( ) because fillInStackTrace( ) produces a handle to a Throwable object. Since Throwable is a base class of Exception, it’s possible to get an object that’s a Throwable but not an Exception, so the handler for Exception in main( ) might miss it. To make sure everything is in order, the compiler forces an exception specification for Throwable. For example, the exception in the following program is not caught in
-
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()");
-
}
-
}
-
} ///:~
-
It’s also possible to rethrow a different exception from the one you caught. If you do this, you get a similar effect as when you use fillInStackTrace( ): the information about the original site of the exception is lost, and what you’re left with is the information pertaining to the new 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");
-
}
-
}
-
} ///:~
-
The output is:
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)
The final exception knows only that it came from main( ), and not from f( ). Note that Throwable isn’t necessary in any of the exception specifications.
You never have to worry about cleaning up the previous exception, or any exceptions for that matter. They’re all heap-based objects created with new, so the garbage collector automatically cleans them all up.