Wanneer gooit Java de ExceptionInInitializerError?

1. Overzicht

In deze korte tutorial gaan we zien waardoor Java een instantie van het ExceptionInInitializerError uitzondering.

We beginnen met een beetje theorie. Dan zien we in de praktijk een paar voorbeelden van deze uitzondering.

2. Het ExceptionInInitializerError

De ExceptionInInitializerError geeft aan dat er een onverwachte uitzondering is opgetreden in een statische initialisatieprogramma. Kortom, als we deze uitzondering zien, moeten we weten dat Java er niet in is geslaagd een statisch initialisatieblok te evalueren of een statische variabele te instantiëren.

Elke keer dat er een uitzondering plaatsvindt in een statische initialisatieprogramma, plaatst Java die uitzondering automatisch in een instantie van de ExceptionInInitializerError klasse. Op deze manier behoudt het ook een verwijzing naar de daadwerkelijke uitzondering als de hoofdoorzaak.

Nu we de grondgedachte achter deze uitzondering kennen, laten we het in de praktijk bekijken.

3. Statisch initialisatieblok

Om een ​​mislukte statische blokinitialisatie te hebben, gaan we opzettelijk een geheel getal door nul delen:

openbare klasse StaticBlock {privé statische int staat; statische {state = 42/0; }}

Als we nu de initialisatie van de klas activeren met zoiets als:

nieuwe StaticBlock ();

Dan zouden we de volgende uitzondering zien:

java.lang.ExceptionInInitializerError bij com.baeldung ... (ExceptionInInitializerErrorUnitTest.java:18) Veroorzaakt door: java.lang.ArithmeticException: / door nul bij com.baeldung.StaticBlock. (ExceptionInInitializerErrorUnitTest.java:35) ...

Zoals eerder vermeld, gooit Java de ExceptionInInitializerError uitzondering met behoud van een verwijzing naar de hoofdoorzaak:

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

Het is ook vermeldenswaard dat de method is een klasse-initialisatiemethode in de JVM.

4. Initialisatie van statische variabelen

Hetzelfde gebeurt als Java er niet in slaagt een statische variabele te initialiseren:

openbare klasse StaticVar {privé statische int state = initializeState (); private static int initializeState () {throw nieuwe RuntimeException (); }}

Nogmaals, als we het initialisatieproces van de klas activeren:

nieuwe StaticVar ();

Dan doet zich dezelfde uitzondering voor:

java.lang.ExceptionInInitializerError bij com.baeldung ... (ExceptionInInitializerErrorUnitTest.java:11) Veroorzaakt door: java.lang.RuntimeException op com.baeldung.StaticVar.initializeState (ExceptionInInitializerErrorUnitTest.java:11) Veroorzaakt door: java.lang.RuntimeException op com.baeldung.StaticVar.initializeState (ExceptionInInitializerErrorUnitTest. ExceptionInInitializerErrorUnitTest.java:23) ... 23 meer

Net als bij statische initialisatieblokken, blijft de hoofdoorzaak van de uitzondering ook behouden:

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

5. Gecontroleerde uitzonderingen

Als onderdeel van de Java-taalspecificatie (JLS-11.2.3) kunnen we aangevinkte uitzonderingen niet binnen een statisch initialisatieblok of statische variabele initialisator gooien. Als we dit bijvoorbeeld proberen:

openbare klasse NoChecked {statisch {throw new Exception (); }}

De compiler zou mislukken met de volgende compilatiefout:

java: initializer moet normaal kunnen worden voltooid

Als afspraak moeten we de mogelijke gecontroleerde uitzonderingen in een instantie van ExceptionInInitializerError wanneer onze statische initialisatielogica een gecontroleerde uitzondering genereert:

openbare klasse CheckedConvention {constructor voor privé-statische constructeurs; statisch {probeer {constructor = CheckedConvention.class.getDeclaredConstructor (); } catch (NoSuchMethodException e) {gooi nieuwe ExceptionInInitializerError (e); }}}

Zoals hierboven getoond, is de getDeclaredConstructor () methode genereert een gecontroleerde uitzondering. Daarom hebben we de gecontroleerde uitzondering opgevangen en ingepakt zoals de conventie suggereert.

Omdat we al een exemplaar van ExceptionInInitializerError uitzondering expliciet, Java zal deze uitzondering niet in nog een andere verpakken ExceptionInInitializerError voorbeeld.

Als we echter een andere ongecontroleerde uitzondering gooien, zal Java een andere genereren ExceptionInInitializerError:

statisch {probeer {constructor = CheckedConvention.class.getConstructor (); } catch (NoSuchMethodException e) {gooi nieuwe RuntimeException (e); }}

Hier verpakken we de aangevinkte uitzondering in een niet-aangevinkte uitzondering. Omdat deze ongecontroleerde uitzondering geen exemplaar is van ExceptionInInitializerError, Java zal het opnieuw inpakken, wat resulteert in deze onverwachte stacktrace:

java.lang.ExceptionInInitializerError bij com.baeldung.exceptionininitializererror ... Veroorzaakt door: java.lang.RuntimeException: java.lang.NoSuchMethodException: ... Veroorzaakt door: java.lang.NoSuchMethodException: com.baeldung.CheckedConvention. () op java.base / java.lang.Class.getConstructor0 (Class.java:3427) op java.base / java.lang.Class.getConstructor (Class.java:2165)

Zoals hierboven getoond, als we de conventie volgen, zou de stacktracering veel schoner zijn dan dit.

5.1. OpenJDK

Sinds kort wordt deze conventie zelfs gebruikt in de OpenJDK-broncode zelf. Hier is bijvoorbeeld hoe de AtomicReference gebruikt deze aanpak:

openbare klasse AtomicReference implementeert java.io.Serializable {privé statische definitieve VarHandle VALUE; statisch {probeer {MethodHandles.Lookup l = MethodHandles.lookup (); WAARDE = l.findVarHandle (AtomicReference.class, "waarde", Object.class); } catch (ReflectiveOperationException e) {gooi nieuwe ExceptionInInitializerError (e); }} privé vluchtige V-waarde; // weggelaten}

6. Conclusie

In deze zelfstudie hebben we gezien waardoor Java een instantie van het ExceptionInInitializerError uitzondering.

Zoals gewoonlijk zijn alle voorbeelden beschikbaar op GitHub.