ClassNotFoundException vs NoClassDefFoundError

1. Úvod

Oboje ClassNotFoundException a NoClassDefFoundError nastať, keď JVM nemôže nájsť v triede požadovanú triedu. Aj keď vyzerajú dobre, sú medzi nimi základné rozdiely.

V tomto výučbe sa dozvieme o niektorých dôvodoch ich výskytu a ich riešeniach.

2. ClassNotFoundException

ClassNotFoundException je kontrolovaná výnimka, ku ktorej dochádza, keď sa aplikácia pokúsi načítať triedu prostredníctvom svojho úplného názvu a nemôže nájsť svoju definíciu v triede cesty.

K tomu dochádza hlavne pri pokuse o načítanie tried pomocou Class.forName (), ClassLoader.loadClass () alebo ClassLoader.findSystemClass (). Preto musíme byť obzvlášť opatrní java.lang.ClassNotFoundException pri práci s odrazom.

Skúsme napríklad načítať triedu ovládačov JDBC bez pridania potrebných závislostí, ktoré nás dostanú ClassNotFoundException:

@Test (očakáva sa = ClassNotFoundException.class) public void givenNoDrivers_whenLoadDriverClass_thenClassNotFoundException () vyvolá ClassNotFoundException {Class.forName ("oracle.jdbc.driver.OracleDriver"); }

3. NoClassDefFoundError

NoClassDefFoundError je fatálna chyba. Nastáva, keď JVM nemôže nájsť definíciu triedy pri pokuse o:

  • Vytvorte inštanciu triedy pomocou Nový kľúčové slovo
  • Načítajte triedu volaním metódy

Chyba nastane, keď kompilátor mohol úspešne zostaviť triedu, ale modulu runtime Java nedokázal nájsť súbor triedy. Spravidla sa to stane, keď existuje výnimka pri vykonávaní statického bloku alebo inicializácii statických polí triedy, takže inicializácia triedy zlyhá.

Uvažujme o scenári, ktorý predstavuje jeden jednoduchý spôsob reprodukcie problému. ClassWithInitErrors inicializácia vyvolá výnimku. Keď sa teda pokúsime vytvoriť objekt ClassWithInitErrors, hodí to ExceptionInInitializerError.

Ak sa pokúsime znova načítať tú istú triedu, dostaneme NoClassDefFoundError:

verejná trieda ClassWithInitErrors {static int data = 1/0; }
verejná trieda NoClassDefFoundErrorExample {verejná ClassWithInitErrors getClassWithInitErrors () {test ClassWithInitErrors; try {test = new ClassWithInitErrors (); } chytit (hoditelne t) {System.out.println (t); } test = new ClassWithInitErrors (); spätný test; }}

Napíšeme testovací prípad pre tento scenár:

@Test (očakáva sa = NoClassDefFoundError.class) public void givenInitErrorInClass_whenloadClass_thenNoClassDefFoundError () {NoClassDefFoundErrorExample sample = new NoClassDefFoundErrorExample (); sample.getClassWithInitErrors (); }

4. Uznesenie

Diagnostikovať a opraviť tieto dva problémy môže byť niekedy časovo náročné. Hlavným dôvodom oboch problémov je nedostupnosť súboru triedy (v ceste triedy) za behu programu.

Pozrime sa na niekoľko prístupov, ktoré môžeme zvážiť pri riešení týchto problémov:

  1. Musíme sa uistiť, či je v triede k dispozícii trieda alebo téglik obsahujúci túto triedu. Ak nie, musíme to pridať
  2. Ak je k dispozícii na triednej ceste aplikácie, pravdepodobne dôjde k prepísaniu triednej cesty. Aby sme to napravili, musíme nájsť presnú cestu k triede použitú našou aplikáciou
  3. Taktiež ak aplikácia používa viac zavádzačov tried, triedy načítané jedným načítavačom tried nemusia byť dostupné inými zavádzačmi tried. Aby sme to dobre vyriešili, je nevyhnutné vedieť, ako fungujú triedové nakladače v Jave

5. Zhrnutie

Aj keď obe tieto výnimky súvisia s classpath a runtime Java, ktorý nedokáže nájsť triedu za behu, je dôležité si uvedomiť ich rozdiely.

Hodiny Java runtime ClassNotFoundException pri pokuse o načítanie triedy iba za behu a meno bolo poskytnuté za behu. V prípade NoClassDefFoundError, trieda bola prítomná v čase kompilácie, ale runtime Java ju nemohol počas behu programu nájsť v Java classpath.

Celý kód pre všetky príklady nájdete ako vždy na serveri GitHub.