Quantcast
Channel: Baeldung
Viewing all articles
Browse latest Browse all 4535

When Does Java Throw the ExceptionInInitializerError?

$
0
0

1. Overview

In this quick tutorial, we're going to see what causes Java to throw an instance of the ExceptionInInitializerError exception.

We'll start with a little bit of theory. Then we'll see a few examples of this exception in practice.

2. The ExceptionInInitializerError

The ExceptionInInitializerError indicates that an unexpected exception has occurred in a static initializerBasically, when we see this exception, we should know that Java failed to evaluate a static initializer block or to instantiate a static variable.

In fact, every time any exception happens inside a static initializer, Java automatically wraps that exception inside an instance of the ExceptionInInitializerError class. This way, it also maintains a reference to the actual exception as the root cause.

Now that we know the rationale behind this exception, let's see it in practice.

3. Static Initializer Block

To have a failed static block initializer, we're going to divide an integer by zero intentionally:

public class StaticBlock {

    private static int state;

    static {
        state = 42 / 0;
    }
}

Now if we trigger the class initialization with something like:

new StaticBlock();

Then, we would see the following exception:

java.lang.ExceptionInInitializerError
    at com.baeldung...(ExceptionInInitializerErrorUnitTest.java:18)
Caused by: java.lang.ArithmeticException: / by zero
    at com.baeldung.StaticBlock.<clinit>(ExceptionInInitializerErrorUnitTest.java:35)
    ... 23 more

As mentioned earlier, Java throws the ExceptionInInitializerError exception while maintaining a reference to the root cause:

assertThatThrownBy(StaticBlock::new)
  .isInstanceOf(ExceptionInInitializerError.class)
  .hasCauseInstanceOf(ArithmeticException.class);

It's also worth mentioning that the <clinit> method is a class initialization method in the JVM.

4. Static Variable Initialization

The same thing happens if Java fails to initialize a static variable:

public class StaticVar {

    private static int state = initializeState();

    private static int initializeState() {
        throw new RuntimeException();
    }
}

Again, if we trigger the class initialization process:

new StaticVar();

Then the same exception occurs:

java.lang.ExceptionInInitializerError
    at com.baeldung...(ExceptionInInitializerErrorUnitTest.java:11)
Caused by: java.lang.RuntimeException
    at com.baeldung.StaticVar.initializeState(ExceptionInInitializerErrorUnitTest.java:26)
    at com.baeldung.StaticVar.<clinit>(ExceptionInInitializerErrorUnitTest.java:23)
    ... 23 more

Similar to static initializer blocks, the root cause of the exception is also preserved:

assertThatThrownBy(StaticVar::new)
  .isInstanceOf(ExceptionInInitializerError.class)
  .hasCauseInstanceOf(RuntimeException.class);

5. Checked Exceptions

As part of the Java language specification (JLS-11.2.3), we can't throw checked exceptions inside a static initializer block or static variable initializer. For instance, if we try to do so:

public class NoChecked {
    static {
        throw new Exception();
    }
}

The compiler would fail with the following compilation error:

java: initializer must be able to complete normally

As a convention, we should wrap the possible checked exceptions inside an instance of ExceptionInInitializerError when our static initialization logic throws a checked exception:

public class CheckedConvention {

    private static Constructor<?> constructor;

    static {
        try {
            constructor = CheckedConvention.class.getDeclaredConstructor();
        } catch (NoSuchMethodException e) {
            throw new ExceptionInInitializerError(e);
        }
    }
}

As shown above, the getDeclaredConstructor() method throws a checked exception. Therefore, we caught the checked exception and wrapped it as the convention suggests.

Since we're already returning an instance of ExceptionInInitializerError exception explicitly, Java won't wrap this exception inside yet another ExceptionInInitializerError instance.

However, if we throw any other unchecked exception, Java will throw another ExceptionInInitializerError:

static {
    try {
        constructor = CheckedConvention.class.getConstructor();
    } catch (NoSuchMethodException e) {
        throw new RuntimeException(e);
    }
}

Here, we're wrapping the checked exception inside an unchecked one. Because this unchecked exception is not an instance of ExceptionInInitializerError, Java will wrap it again, resulting in this unexpected stack trace:

java.lang.ExceptionInInitializerError
	at com.baeldung.exceptionininitializererror...
Caused by: java.lang.RuntimeException: java.lang.NoSuchMethodException: ...
Caused by: java.lang.NoSuchMethodException: com.baeldung.CheckedConvention.<init>()
	at java.base/java.lang.Class.getConstructor0(Class.java:3427)
	at java.base/java.lang.Class.getConstructor(Class.java:2165)

As shown above, if we follow the convention, then the stack trace would be much cleaner than this.

5.1. OpenJDK

Recently, this convention is even used in the OpenJDK source code itself. For instance, here's how the AtomicReference is using this approach:

public class AtomicReference<V> implements java.io.Serializable {
    private static final VarHandle VALUE;
    static {
        try {
            MethodHandles.Lookup l = MethodHandles.lookup();
            VALUE = l.findVarHandle(AtomicReference.class, "value", Object.class);
        } catch (ReflectiveOperationException e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    private volatile V value;

   // omitted
}

6. Conclusion

In this tutorial, we saw what causes Java to throw an instance of the ExceptionInInitializerError exception.

As usual, all the examples are available over on GitHub.


Viewing all articles
Browse latest Browse all 4535

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>