Java >> Java tutorial >  >> Tag >> throw

Hvornår kaster Java ExceptionInInitializerError?

1. Oversigt

I denne hurtige selvstudie skal vi se, hvad der får Java til at kaste en forekomst af ExceptionInInitializerError  undtagelse.

Vi starter med en lille smule teori. Så vil vi se et par eksempler på denne undtagelse i praksis.

2. ExceptionInInitializerError

ExceptionInInitializerError  angiver, at der er opstået en uventet undtagelse i en statisk initialisering. Dybest set, når vi ser denne undtagelse, bør vi vide, at Java ikke kunne evaluere en statisk initialiseringsblok eller at instantiere en statisk variabel.

Faktisk, hver gang der sker en undtagelse inde i en statisk initializer, indpakker Java automatisk denne undtagelse i en forekomst af ExceptionInInitializerError klasse. På denne måde opretholder den også en reference til den faktiske undtagelse som grundårsagen.

Nu hvor vi kender rationalet bag denne undtagelse, lad os se det i praksis.

3. Statisk initialiseringsblok

For at have en mislykket statisk blokinitialisering, skal vi med vilje dividere et heltal med nul:

public class StaticBlock {

    private static int state;

    static {
        state = 42 / 0;
    }
}

Hvis vi nu udløser klassens initialisering med noget som:

new StaticBlock();

Så vil vi se følgende undtagelse:

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

Som tidligere nævnt afgiver Java ExceptionInInitializerError  undtagelse, mens du opretholder en reference til den grundlæggende årsag:

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

Det er også værd at nævne, at   metode er en klasseinitialiseringsmetode i JVM.

4. Statisk variabel initialisering

Det samme sker, hvis Java ikke kan initialisere en statisk variabel:

public class StaticVar {

    private static int state = initializeState();

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

Igen, hvis vi udløser klasseinitialiseringsprocessen:

new StaticVar();

Så sker den samme undtagelse:

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

I lighed med statiske initialiseringsblokke bevares årsagen til undtagelsen også:

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

5. Markerede undtagelser

Som en del af Java-sprogspecifikationen (JLS-11.2.3), kan vi ikke smide kontrollerede undtagelser inde i en statisk initialiseringsblok eller en statisk variabel initialisering. For eksempel, hvis vi forsøger at gøre det:

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

Compileren ville mislykkes med følgende kompileringsfejl:

java: initializer must be able to complete normally

Som en konvention bør vi indpakke de mulige kontrollerede undtagelser i en forekomst af ExceptionInInitializerError  når vores statiske initialiseringslogik kaster en markeret undtagelse:

public class CheckedConvention {

    private static Constructor<?> constructor;

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

Som vist ovenfor er getDeclaredConstructor()  metode kaster en kontrolleret undtagelse. Derfor fangede vi den kontrollerede undtagelse og pakkede den ind, som konventionen foreslår.

Da vi allerede returnerer en forekomst af ExceptionInInitializerError undtagelse udtrykkeligt, vil Java ikke indpakke denne undtagelse i endnu en ExceptionInInitializerError  forekomst.

Men hvis vi kaster en anden umarkeret undtagelse, vil Java kaste en anden ExceptionInInitializerError :

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

Her pakker vi den markerede undtagelse ind i en umarkeret. Fordi denne umarkerede undtagelse ikke er en forekomst af ExceptionInInitializerError,  Java vil indpakke det igen, hvilket resulterer i denne uventede staksporing:

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)

Som vist ovenfor, hvis vi følger konventionen, ville staksporet være meget renere end dette.

5.1. OpenJDK

For nylig er denne konvention endda brugt i selve OpenJDK-kildekoden. For eksempel, her er, hvordan AtomicReference  bruger denne tilgang:

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

I dette selvstudie så vi, hvad der får Java til at kaste en forekomst af ExceptionInInitializerError  undtagelse.

Som sædvanlig er alle eksemplerne tilgængelige på GitHub.


Java tag