First of all I’d like to announce, that (as a result to a suggestion for improvement) it’s not necessary any more to subscribe in order to post comments on this blog - so please feel free to put your oar in (you too, Mario)!
Ok, todays post started off as a question from my colleague Chris about what would happen in Java if an Exception was thrown in the constructor. And it ended with, well … read for yourself.
After we decided that Java’s behaviour in case of an Exception in the constructor was still quite decent, the next step was to have a look at class initialization - what happens if an Exception is thrown in the static initializer for example.
First Attempt: we try to throw a RuntimeException (something gives me the feeling, that only an unchecked Exception will work here):
...
static {
throw new RuntimeException("oops");
}
...
Hey, not even a RuntimeException works. The compiler complains about the initializer not completing normally. Of course not, stupid compiler! That’s what we wanted!
But we wouldn’t be in the software business if we hadn’t fooled the compiler once or twice.
Second Attempt:
...
static {
if (true)
throw new RuntimeException("oops");
}
...
Gotcha, smart ass! When we try to run our class (added a main method for that), we get an java.lang.ExceptionInInitializerError. That’s what we wanted.
Third Attempt:
Not much unexpected behaviour yet. What can we do now. Hmmm … I remember someone saying, that it’s a bad idea in a constructor to expose this (the object currently being constructed) to another object (or even thread).
Ok, if that’s a bad idea, then exposing an object from inside the static initializer must be a very bad idea. So, let’s go.
A “good” way to expose an object is to register it in some registry. So we start with this registry:
public class Registry {
private static SelfRegistered registered;
public static void main(String [] args) {
try {
SelfRegistered.init();
} catch (ExceptionInInitializerError err) {
err.printStackTrace();
}
registered.hello();
SelfRegistered registered2 = new SelfRegistered();
registered2.hello();
}
public static void register(SelfRegistered selfRegistered) {
registered = selfRegistered;
}
}
And then the class, that creates an instance in the static initializer and registers it in the registry.
public class SelfRegistered {
static {
Registry.register(new SelfRegistered());
// in some cases, something happens
if (false) throw new RuntimeException("oops!");
}
public static void init() {
// do some init stuff
}
public void hello() {
System.out.println("Hello from " + this);
}
}
So if we run the Registry class, the SelfRegistered.init() method is called, which does actually nothing, except that it of course causes the SelfRegistered class to be loaded and initialized. So before the init method does its nothing, the static initializer is executed, creating a new SelfRegistered() and registering it in the registry. The Exception is of course not thrown, because the condition can never evaluate to true.
Then, after the static initializer and the init method, we are back in the Registry.main method, where the hello method is first called on the registered object, and then on another instance of the SelfRegistered class.
We run it, and we get something like:
Hello from SelfRegistered@57f0dc
Hello from SelfRegistered@32c41a
Last Attempt:I have the feeling, that we are on the right way, so let’s quickly see what happens if the RuntimeException is thrown in the static initializer. So change the line in SelfRegistered to:
if (true) throw new RuntimeException(”oops!”);
… and run it!
Wow … the output looks like this:
java.lang.ExceptionInInitializerError
at Registry.main(Registry.java:8)
Caused by: java.lang.RuntimeException: oops!
at SelfRegistered.(SelfRegistered.java:7)
… 1 more
Hello from SelfRegistered@6a55fa
Exception in thread “main” java.lang.NoClassDefFoundError
at Registry.main(Registry.java:13)
So if we carefully read this (with the other eye on our code), we see that during initialization of the SelfRegistered, after registering an instance of this class in the Registry, the RuntimeException was thrown, which caused class initialization to abort.
After catching this error, we try to call a method on the registered object, and - surprise surprise - it works!
But the class cannot be completely initialized, can it? The static initializer failed!
Well, of course it is not initialized, as we can see in the next lines, where we are trying to create a new instance of SelfRegistered - which fails.
The Moral:
What we learn here is, that it’s usually not a very good idea to expose an object from inside a constructor or even static initializer. This gets even more dangerous if Exceptions may occur (which is practically everywhere, as we know).
But the most important lesson is that even Java does not always behave the way we would like it to …