Trying to throw a checked exception from a static block is also not possible. We can have a try and catch block in a static block where a checked exception may be thrown from the try block but we have to resolve it within the catch block. We can't propagate it further using a throw keyword.
Exceptions in static block Just like any other method in Java when an exception occurs in static block you can handle it using try-catch pair.
Checked Exceptions in Java A checked exception is caught at compile time so if something throws a checked exception the compiler will enforce that you handle it.
Java does not allows static initialization block to throw any exception, though it is allowed to use use try-catch block inside static initialization block.
Because it is not possible to handle these checked exceptions in your source. You do not have any control over the initialization process and static{} blocks cannot be called from your source so that you could surround them with try-catch.
Because you cannot handle any error indicated by a checked exception, it was decided to disallow throwing of checked exceptions static blocks.
The static block must not throw checked exceptions but still allows unchecked/runtime-exceptions to be thrown. But according to above reasons you would be unable to handle these either.
To summarize, this restriction prevents (or at least makes it harder for) the developer from building something which can result in errors from which the application would be unable to recover.
You can work around the problem by catching any checked exception and rethrowing it as an unchecked exception. This unchecked exception class works well as a wrapper: java.lang.ExceptionInInitializerError
.
Sample code:
protected static class _YieldCurveConfigHelperSingleton {
public static YieldCurveConfigHelper _staticInstance;
static {
try {
_staticInstance = new YieldCurveConfigHelper();
}
catch (IOException | SAXException | JAXBException e) {
throw new ExceptionInInitializerError(e);
}
}
}
It would have to look like this (this is not valid Java code)
// Not a valid Java Code
static throws SomeCheckedException {
throw new SomeCheckedException();
}
but how would ad where you catch it? Checked exceptions require catching. Imagine some examples that may initialize the class (or may not because it is already initialized), and just to draw the attention of the complexity of that it would introduce, I put the examples in another static initalizer:
static {
try {
ClassA a = new ClassA();
Class<ClassB> clazz = Class.forName(ClassB.class);
String something = ClassC.SOME_STATIC_FIELD;
} catch (Exception oops) {
// anybody knows which type might occur?
}
}
And another nasty thing -
interface MyInterface {
final static ClassA a = new ClassA();
}
Imagine ClassA had a static initializer throwing a checked exception: In this case MyInterface (which is an interface with a 'hidden' static initializer) would have to throw the exception or handle it - exception handling at an interface? Better leave it as it is.
Why doesn't Java allow to throw a checked exception from a static initialization block?
Technically, you can do this. However, the checked exception must be caught within the block.
The actual Java restriction is that a checked exception is not allowed to propagate out of the block.
Technically, it is also possible to allow an unchecked exception to propagate out of a static initializer block1. But it is a really bad idea to do this deliberately! The problem is that the JVM itself catches the unchecked exception, and wraps it and rethrows it as a ExceptionInInitializerError
.
NB: that ExceptionInInitializerError
is an Error
not a regular exception. You should not attempt to recover from it.
In most cases, the exception cannot be caught:
public class Test {
static {
int i = 1;
if (i == 1) {
throw new RuntimeException("Bang!");
}
}
public static void main(String[] args) {
try {
// stuff
} catch (Throwable ex) {
// This won't be executed.
System.out.println("Caught " + ex);
}
}
}
$ java Test
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.RuntimeException: Bang!
at Test.<clinit>(Test.java:5)
There is nowhere you can place a try ... catch
in the above to catch the ExceptionInInitializerError
2.
In some cases you can catch it. For example, if you triggered the class initialization by calling Class.forName(...)
, you can enclose the call in a try
and catch either the ExceptionInInitializerError
or a subsequent NoClassDefFoundError
.
However, if you attempt to recover from an ExceptionInInitializerError
you are liable to run into a roadblock. The problem is that before throwing the error, the JVM marks the class that caused the problem as "failed". You simply won't be able to use it. Furthermore, any other classes that depend on the failed class will also go into failed state if they attempt to initialize. The only way forward is to unload all of the failed classes. That might be feasible for dynamically loaded code3, but in general it isn't.
1 - It is a compilation error if a static block unconditionally throws an unchecked exception.
2 - You might be able to intercept it by registering a default uncaught exception handler, but that won't allow you to recover, because your "main" thread can't start.
3 - If you wanted to recover the failed classes, you would need to get rid of the classloader that loaded them.
What was the reason behind this design decision?
It is to protect the programmer from writing code that throws exceptions that cannot be handled ... because there is no way for the programmer to write a handler.
As we have seen, an exception in a static initializer turns a typical application into a brick. The best thing that the language designers can do help the the programmer is to specified that the checked case 1s a compilation error. Unfortunately, it is not practical to do this for unchecked exceptions as well.
OK, so what should you do if your code "needs" to throw exceptions in a static initializer. Basically, there are two alternatives:
If (full!) recovery from the exception within the block is possible, then do that.
Otherwise, restructure your code so that the initialization doesn't happen in a static initialization block (or in the initializers of static variables). Put it in a method or constructor that can be called from a regular thread.
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