Quando Java genera l'eccezione ExceptionInInitializerError?

1. Panoramica

In questo breve tutorial, vedremo cosa fa sì che Java lanci un'istanza dell'eccezione ExceptionInInitializerError .

Inizieremo con un po 'di teoria. Quindi vedremo alcuni esempi di questa eccezione nella pratica.

2. L' eccezione ExceptionInInitializerError

L' eccezione ExceptionInInitializerError indica che si è verificata un'eccezione imprevista in un inizializzatore statico. Fondamentalmente, quando vediamo questa eccezione, dovremmo sapere che Java non è riuscito a valutare un blocco di inizializzazione statico o ad istanziare una variabile statica.

Infatti, ogni volta che si verifica un'eccezione all'interno di un inizializzatore statico, Java avvolge automaticamente tale eccezione all'interno di un'istanza della classe ExceptionInInitializerError . In questo modo, mantiene anche un riferimento all'eccezione effettiva come causa principale.

Ora che conosciamo la logica alla base di questa eccezione, vediamo in pratica.

3. Blocco inizializzatore statico

Per avere un inizializzatore di blocco statico fallito, divideremo intenzionalmente un intero per zero:

public class StaticBlock { private static int state; static { state = 42 / 0; } }

Ora se attiviamo l'inizializzazione della classe con qualcosa di simile:

new StaticBlock();

Quindi, vedremmo la seguente eccezione:

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

Come accennato in precedenza, Java genera l' eccezione ExceptionInInitializerError mantenendo un riferimento alla causa principale:

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

Vale anche la pena ricordare che il file è un metodo di inizializzazione della classe nella JVM.

4. Inizializzazione della variabile statica

La stessa cosa accade se Java non riesce a inizializzare una variabile statica:

public class StaticVar { private static int state = initializeState(); private static int initializeState() { throw new RuntimeException(); } }

Di nuovo, se attiviamo il processo di inizializzazione della classe:

new StaticVar();

Quindi si verifica la stessa eccezione:

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

Simile ai blocchi di inizializzazione statici, viene preservata anche la causa principale dell'eccezione:

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

5. Eccezioni verificate

Come parte della specifica del linguaggio Java (JLS-11.2.3), non possiamo generare eccezioni controllate all'interno di un blocco di inizializzazione statica o di un inizializzatore di variabile statica. Ad esempio, se proviamo a farlo:

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

Il compilatore fallirà con il seguente errore di compilazione:

java: initializer must be able to complete normally

Per convenzione, dovremmo racchiudere le possibili eccezioni verificate all'interno di un'istanza di ExceptionInInitializerError quando la nostra logica di inizializzazione statica genera un'eccezione controllata:

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

Come mostrato sopra, il metodo getDeclaredConstructor () genera un'eccezione controllata. Pertanto, abbiamo intercettato l'eccezione selezionata e l'abbiamo inserita come suggerisce la convenzione.

Poiché stiamo già restituendo un'istanza dell'eccezione ExceptionInInitializerError in modo esplicito, Java non racchiuderà questa eccezione in un'altra istanza ExceptionInInitializerError .

Tuttavia, se lanciamo qualsiasi altra eccezione non selezionata, Java lancerà un altro ExceptionInInitializerError :

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

Qui, stiamo avvolgendo l'eccezione selezionata in una non selezionata. Poiché questa eccezione non selezionata non è un'istanza di ExceptionInInitializerError, Java lo avvolgerà di nuovo, risultando in questa traccia imprevista dello stack:

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)

Come mostrato sopra, se seguiamo la convenzione, la traccia dello stack sarebbe molto più pulita di questa.

5.1. OpenJDK

Recentemente, questa convenzione è stata utilizzata anche nel codice sorgente di OpenJDK stesso. Ad esempio, ecco come AtomicReference utilizza questo approccio:

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. Conclusione

In questo tutorial, abbiamo visto cosa fa sì che Java generi un'istanza dell'eccezione ExceptionInInitializerError .

Come al solito, tutti gli esempi sono disponibili su GitHub.