1. Información general
En este tutorial rápido, veremos qué hace que Java lance una instancia de la excepción ExceptionInInitializerError .
Empezaremos con un poco de teoría. Luego veremos algunos ejemplos de esta excepción en la práctica.
2. El error ExceptionInInitializerError
El ExceptionInInitializerError indica que una excepción inesperado se ha producido en un inicializador estático. Básicamente, cuando vemos esta excepción, debemos saber que Java no pudo evaluar un bloque inicializador estático o instanciar una variable estática.
De hecho, cada vez que ocurre una excepción dentro de un inicializador estático, Java envuelve automáticamente esa excepción dentro de una instancia de la clase ExceptionInInitializerError . De esta manera, también mantiene una referencia a la excepción real como causa raíz.
Ahora que conocemos el fundamento de esta excepción, veámoslo en la práctica.
3. Bloque inicializador estático
Para tener un inicializador de bloque estático fallido, vamos a dividir un entero por cero intencionalmente:
public class StaticBlock { private static int state; static { state = 42 / 0; } }
Ahora, si activamos la inicialización de la clase con algo como:
new StaticBlock();
Entonces, veríamos la siguiente excepción:
java.lang.ExceptionInInitializerError at com.baeldung...(ExceptionInInitializerErrorUnitTest.java:18) Caused by: java.lang.ArithmeticException: / by zero at com.baeldung.StaticBlock.(ExceptionInInitializerErrorUnitTest.java:35) ... 23 more
Como se mencionó anteriormente, Java lanza la excepción ExceptionInInitializerError mientras mantiene una referencia a la causa raíz:
assertThatThrownBy(StaticBlock::new) .isInstanceOf(ExceptionInInitializerError.class) .hasCauseInstanceOf(ArithmeticException.class);
También vale la pena mencionar que el El método es un método de inicialización de clases en la JVM.
4. Inicialización de variable estática
Lo mismo sucede si Java no puede inicializar una variable estática:
public class StaticVar { private static int state = initializeState(); private static int initializeState() { throw new RuntimeException(); } }
Nuevamente, si activamos el proceso de inicialización de la clase:
new StaticVar();
Entonces ocurre la misma excepción:
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.(ExceptionInInitializerErrorUnitTest.java:23) ... 23 more
De forma similar a los bloques inicializadores estáticos, también se conserva la causa raíz de la excepción:
assertThatThrownBy(StaticVar::new) .isInstanceOf(ExceptionInInitializerError.class) .hasCauseInstanceOf(RuntimeException.class);
5. Excepciones marcadas
Como parte de la especificación del lenguaje Java (JLS-11.2.3), no podemos lanzar excepciones marcadas dentro de un bloque inicializador estático o un inicializador de variable estática. Por ejemplo, si intentamos hacerlo:
public class NoChecked { static { throw new Exception(); } }
El compilador fallaría con el siguiente error de compilación:
java: initializer must be able to complete normally
Como convención, deberíamos envolver las posibles excepciones marcadas dentro de una instancia de ExceptionInInitializerError cuando nuestra lógica de inicialización estática arroja una excepción marcada:
public class CheckedConvention { private static Constructor constructor; static { try { constructor = CheckedConvention.class.getDeclaredConstructor(); } catch (NoSuchMethodException e) { throw new ExceptionInInitializerError(e); } } }
Como se muestra arriba, el método getDeclaredConstructor () lanza una excepción marcada. Por lo tanto, capturamos la excepción marcada y la ajustamos como sugiere la convención.
Dado que ya estamos devolviendo una instancia de la excepción ExceptionInInitializerError explícitamente, Java no incluirá esta excepción dentro de otra instancia de ExceptionInInitializerError .
Sin embargo, si lanzamos cualquier otra excepción sin marcar, Java arrojará otro ExceptionInInitializerError :
static { try { constructor = CheckedConvention.class.getConstructor(); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } }
Aquí, estamos envolviendo la excepción marcada dentro de una no marcada. Debido a que esta excepción sin marcar no es una instancia de ExceptionInInitializerError, Java la ajustará nuevamente, lo que dará como resultado este seguimiento de pila inesperado:
java.lang.ExceptionInInitializerError at com.baeldung.exceptionininitializererror... Caused by: java.lang.RuntimeException: java.lang.NoSuchMethodException: ... Caused by: java.lang.NoSuchMethodException: com.baeldung.CheckedConvention.() at java.base/java.lang.Class.getConstructor0(Class.java:3427) at java.base/java.lang.Class.getConstructor(Class.java:2165)
Como se muestra arriba, si seguimos la convención, entonces el seguimiento de la pila sería mucho más limpio que esto.
5.1. OpenJDK
Recientemente, esta convención se usa incluso en el código fuente de OpenJDK. Por ejemplo, así es como AtomicReference utiliza este enfoque:
public class AtomicReference 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. Conclusión
En este tutorial, vimos qué hace que Java lance una instancia de la excepción ExceptionInInitializerError .
Como de costumbre, todos los ejemplos están disponibles en GitHub.