Kedy Java vyhodí chybu ExceptionInInitializerError?

1. Prehľad

V tomto rýchlom výučbe sa dozvieme, čo spôsobí, že Java hodí inštanciu súboru ExceptionInInitializerError výnimkou.

Začneme trochou teórie. Potom uvidíme niekoľko príkladov tejto výnimky v praxi.

2. The ExceptionInInitializerError

The ExceptionInInitializerError označuje, že v statickom inicializátore došlo k neočakávanej výnimke. V zásade, keď vidíme túto výnimku, mali by sme vedieť, že Java nedokázala vyhodnotiť blok statického inicializátora alebo vytvoriť inštanciu statickej premennej.

V skutočnosti zakaždým, keď sa vyskytne akákoľvek výnimka vo vnútri statického inicializátora, Java túto výnimku automaticky zabalí do inštancie ExceptionInInitializerError trieda. Týmto spôsobom tiež udržuje odkaz na skutočnú výnimku ako hlavnú príčinu.

Teraz, keď poznáme dôvody tejto výnimky, sa pozrime na to v praxi.

3. Blok statického inicializátora

Ak chceme mať neúspešný inicializátor statického bloku, vydelíme zámerne celé číslo nulou:

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

Teraz, keď spustíme inicializáciu triedy pomocou niečoho ako:

nový StaticBlock ();

Potom by sme videli nasledujúcu výnimku:

java.lang.ExceptionInInitializerError na com.baeldung ... (ExceptionInInitializerErrorUnitTest.java:18) Spôsobené: java.lang.ArithmeticException: / nulou na com.baeldung.StaticBlock. (ExceptionInInitializerErrorUnitTest.java:35) ... 23 more

Ako už bolo spomenuté skôr, Java hodí ExceptionInInitializerError výnimku pri zachovaní odkazu na hlavnú príčinu:

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

Za zmienku tiež stojí, že metóda je metóda inicializácie triedy v JVM.

4. Inicializácia statickej premennej

To isté sa stane, ak Java nedokáže inicializovať statickú premennú:

verejná trieda StaticVar {private static int state = initializeState (); private static int initializeState () {throw new RuntimeException (); }}

Opäť platí, že ak spustíme proces inicializácie triedy:

new StaticVar ();

Potom nastane rovnaká výnimka:

java.lang.ExceptionInInitializerError na com.baeldung ... (ExceptionInInitializerErrorUnitTest.java:11) Spôsobené: java.lang.RuntimeException na com.baeldung.StaticVar.initializeState (ExceptionInInitializerErrorUnitTest.java:26) att ExceptionInInitializerErrorUnitTest.java:23) ... 23 ďalších

Podobne ako v prípade statických inicializačných blokov je zachovaná aj hlavná príčina výnimky:

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

5. Začiarknuté výnimky

V rámci špecifikácie jazyka Java (JLS-11.2.3) nemôžeme hádzať kontrolované výnimky do bloku statického inicializátora alebo inicializátora statických premenných. Napríklad, ak sa o to pokúsime:

verejná trieda NoChecked {static {throw new Exception (); }}

Kompilátor by zlyhal s nasledujúcou chybou kompilácie:

java: inicializátor musí byť schopný dokončenia normálne

Ako konvenciu by sme mali zabaliť možné kontrolované výnimky do inštancie ExceptionInInitializerError keď naša logika statickej inicializácie vyhodí kontrolovanú výnimku:

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

Ako je uvedené vyššie, getDeclaredConstructor () metóda vyvolá zaškrtnutú výnimku. Preto sme chytili skontrolovanú výnimku a zabalili ju tak, ako to naznačuje dohovor.

Pretože už vraciame inštanciu ExceptionInInitializerError výslovne výnimkou, Java túto výnimku nezabalí do iného ExceptionInInitializerError inštancia.

Ak však hodíme inú nezaškrtnutú výnimku, Java hodí ďalšiu ExceptionInInitializerError:

static {try {constructor = CheckedConvention.class.getConstructor (); } catch (NoSuchMethodException e) {hodiť novú RuntimeException (e); }}

Tu zabalíme začiarknutú výnimku do neoznačenej. Pretože táto nekontrolovaná výnimka nie je inštanciou ExceptionInInitializerError, Java to zabalí znova, čo povedie k tomuto neočakávanému sledovaniu zásobníka:

java.lang.ExceptionInInitializerError na adrese com.baeldung.exceptionininitializererror ... Spôsobené: java.lang.RuntimeException: java.lang.NoSuchMethodException: ... Spôsobené: java.lang.NoSuchMethodException: com.baeldung.CheckedConvention. java.base / java.lang.Class.getConstructor0 (Class.java:3427) na java.base / java.lang.Class.getConstructor (Class.java:2165)

Ako je uvedené vyššie, ak sa budeme riadiť konvenciou, potom by bolo sledovanie zásobníka oveľa čistejšie.

5.1. OpenJDK

Nedávno sa táto konvencia používa aj v samotnom zdrojovom kóde OpenJDK. Napríklad takto Atómová referencia používa tento prístup:

verejná trieda AtomicReference implementuje java.io.Serializable {private static final VarHandle VALUE; static {try {MethodHandles.Lookup l = MethodHandles.lookup (); VALUE = l.findVarHandle (AtomicReference.class, "hodnota", Object.class); } catch (ReflectiveOperationException e) {throw new ExceptionInInitializerError (e); }} private volatile V value; // vynechané}

6. Záver

V tomto návode sme videli, čo spôsobuje, že Java hodí inštanciu súboru ExceptionInInitializerError výnimkou.

Ako obvykle sú všetky príklady dostupné na GitHub.


$config[zx-auto] not found$config[zx-overlay] not found