Why doesn't Java allow to throw a checked exception from a static initialization block? What was the reason behind this design decision?
问题:
回答1:
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.
回答2:
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);
}
}
}
回答3:
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.
回答4:
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. 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 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!
As we have seen, an exception in a static initializer turns a typical application into a brick. The best thing think that the language designers could do is to deal with the checked case as 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).
回答5:
Take a look at the Java Language Specifications: it is stated that it is a compile time error if static initializer fails is able to complete abruptly with a checked exception.
回答6:
Since no code you write can call static initialization block, it is not useful to throw checked exceptions
. If it were possible, what would the jvm do when a checked exceptions are thrown? Runtimeexceptions
are propagated up.
回答7:
I am able to compile throwing a checked Exception Also....
static {
try {
throw new IOException();
} catch (Exception e) {
// Do Something
}
}