Is it safe to:
public class Widget {
private static final IllegalStateException LE_EXCEPTION
= new IllegalStateException("le sophisticated way");
...
public void fun() {
// some logic here, that may throw
throw LE_EXCEPTION;
}
....
}
instead of throwing a new
exception each time?
I am interested if it's safe
By safe I mean: no memory corruption, no additional exceptions thrown by JVM, by missing classes, no bad classes loaded (...). Note: the exception will be thrown over network (remoting).
Other issues (readability, cost of keeping an instance) are not important.
What will happen to the Exception object after exception handling ? The Exception object will be garbage collected in the next garbage collection.
Re-throwing ExceptionsWhen an exception is caught, we can perform some operations, like logging the error, and then re-throw the exception. Re-throwing an exception means calling the throw statement without an exception object, inside a catch block. It can only be used inside a catch block.
You should handle the exception at the lowest possible level. If method can't handle the exception properly you should throw it.
Java provides two different options to handle an exception. You can either use the try-catch-finally approach to handle all kinds of exceptions. Or you can use the try-with-resource approach which allows an easier cleanup process for resources.
It depends on your definition of "safe." The exception will give a misleading stack trace, which I wouldn't call "safe". Consider:
public class ThrowTest {
private static Exception e = new Exception("t1"); // Line 2
public static final void main(String[] args) {
ThrowTest tt;
tt = new ThrowTest();
try {
tt.t1();
}
catch (Exception ex) {
System.out.println("t1:");
ex.printStackTrace(System.out);
}
try {
tt.t2(); // Line 16
}
catch (Exception ex) {
System.out.println("t2:");
ex.printStackTrace(System.out);
}
}
private void t1()
throws Exception {
throw this.e;
}
private void t2()
throws Exception {
throw new Exception("t2"); // Line 31
}
}
That has this output:
$ java ThrowTest t1: java.lang.Exception: t1 at ThrowTest.<clinit>(ThrowTest.java:2) t2: java.lang.Exception: t2 at ThrowTest.t2(ThrowTest.java:31) at ThrowTest.main(ThrowTest.java:16)
Note how the t1
method is completely missing from the stack trace in the first test case. There's no useful context information at all.
Now, you can use fillInStackTrace
to fill in that information just before the throw:
this.e.fillInStackTrace();
throw this.e;
...but that's just making work for yourself (work you will forget sometimes). There's simply no benefit to it at all. And not all exceptions allow you to do it (some exceptions make the stack trace read-only).
You've said elsewhere in the comments that this is to avoid "code duplication." You're much better off having an exception builder function:
private IllegalStateException buildISE() {
return new IllegalStateException("le sophisticated way");
}
(Could be static final
if you like.)
And then throwing it like this:
throw buildISE();
That avoids code duplication without misleading stack traces and unnecessary Exception instances.
Here's what that looks like applied to the above:
public class ThrowTest {
public static final void main(String[] args) {
ThrowTest tt;
tt = new ThrowTest();
try {
tt.t1(); // Line 8
}
catch (Exception ex) {
System.out.println("t1:");
ex.printStackTrace(System.out);
}
try {
tt.t2(); // Line 15
}
catch (Exception ex) {
System.out.println("t2:");
ex.printStackTrace(System.out);
}
}
private static final Exception buildEx() {
return new Exception("t1"); // Line 24
}
private void t1()
throws Exception {
throw buildEx(); // Line 29
}
private void t2()
throws Exception {
throw new Exception("t2"); // Line 34
}
}
$ java ThrowTest t1: java.lang.Exception: t1 at ThrowTest.buildEx(ThrowTest.java:24) at ThrowTest.t1(ThrowTest.java:29) at ThrowTest.main(ThrowTest.java:8) t2: java.lang.Exception: t2 at ThrowTest.t2(ThrowTest.java:34) at ThrowTest.main(ThrowTest.java:15)
It is not safe, unless the exception is immutable (i.e. enableSuppression=writableStackTrace=false).
If an exception is not immutable, it can be modified by a catcher - setting a new stacktrace or adding a suppressed exception. If there are multiple catchers trying to modify the exception, there'll be chaos.
Astonishingly, Throwable
is actually thread-safe, for god-knows-what. So at least there won't be catastrophic failure if an exception is modified by multiple threads. But there will be logic failure.
Memory leak is also possible, if app keeps adding suppressed exceptions to this long-live exception.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With