1. Въведение
И ClassNotFoundException, и NoClassDefFoundError възникват, когато JVM не може да намери заявен клас в пътя на класа. Въпреки че изглеждат познати, има някои основни разлики между тези две.
В този урок ще обсъдим някои от причините за възникването им и техните решения.
2. ClassNotFoundException
ClassNotFoundException е проверено изключение, което се случва, когато приложението се опитва да зареди клас чрез напълно квалифицираното му име и не може да намери дефиницията му в пътя на класа.
Това се случва главно при опит за зареждане на класове с Class.forName () , ClassLoader.loadClass () или ClassLoader.findSystemClass () . Следователно трябва да бъдем особено внимателни с java.lang.ClassNotFoundException, докато работим с отражение.
Например, нека се опитаме да заредим класа на JDBC драйвер, без да добавяме необходимите зависимости, които ще ни доведат до ClassNotFoundException:
@Test(expected = ClassNotFoundException.class) public void givenNoDrivers_whenLoadDriverClass_thenClassNotFoundException() throws ClassNotFoundException { Class.forName("oracle.jdbc.driver.OracleDriver"); }
3. NoClassDefFoundError
NoClassDefFoundError е фатална грешка. Това се случва, когато JVM не може да намери дефиницията на класа, докато се опитва да:
- Инстанцирайте клас, като използвате новата ключова дума
- Заредете клас с извикване на метод
Грешката възниква, когато компилаторът може успешно да компилира класа, но изпълнението на Java не може да намери файла на класа. Обикновено това се случва, когато има изключение при изпълнение на статичен блок или инициализиране на статични полета на класа, така че инициализацията на класа е неуспешна.
Нека разгледаме сценарий, който е един прост начин за възпроизвеждане на проблема. Инициализацията на ClassWithInitErrors хвърля изключение. И така, когато се опитваме да създадем обект от ClassWithInitErrors, той изхвърля ExceptionInInitializerError.
Ако се опитаме да заредим отново същия клас, получаваме NoClassDefFoundError:
public class ClassWithInitErrors { static int data = 1 / 0; }
public class NoClassDefFoundErrorExample { public ClassWithInitErrors getClassWithInitErrors() { ClassWithInitErrors test; try { test = new ClassWithInitErrors(); } catch (Throwable t) { System.out.println(t); } test = new ClassWithInitErrors(); return test; } }
Нека напишем тестов случай за този сценарий:
@Test(expected = NoClassDefFoundError.class) public void givenInitErrorInClass_whenloadClass_thenNoClassDefFoundError() { NoClassDefFoundErrorExample sample = new NoClassDefFoundErrorExample(); sample.getClassWithInitErrors(); }
4. Резолюция
Понякога диагностицирането и отстраняването на тези два проблема може да отнеме доста време. Основната причина и за двата проблема е недостъпността на файла на класа (в пътя на класа) по време на изпълнение.
Нека да разгледаме няколко подхода, които можем да разгледаме, когато се занимаваме с някой от следните:
- Трябва да се уверим дали клас или буркан, съдържащ този клас, са налични в пътя на класа. Ако не, трябва да го добавим
- Ако е наличен в classpath на приложението, тогава най-вероятно classpath става заменен. За да определим, че трябва да намерим точния път на класа, използван от нашето приложение
- Също така, ако дадено приложение използва зареждачи с няколко класа, класовете, заредени от един loadloader, може да не са налични от други зареждащи класове. За да отстраните проблема добре, от съществено значение е да знаете как зареждачите на класове работят в Java
5. Обобщение
Въпреки че и двете тези изключения са свързани с classpath и Java runtime, които не могат да намерят клас по време на изпълнение, важно е да се отбележат разликите им.
Java runtime изхвърля ClassNotFoundException, докато се опитва да зареди клас само по време на изпълнение и името е предоставено по време на изпълнение. В случая на NoClassDefFoundError, класът присъства по време на компилация, но Java runtime не може да го намери в Java classpath по време на изпълнение.
Както винаги, пълният код за всички примери може да бъде намерен в GitHub.