«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. Решение

Иногда , диагностика и устранение этих двух проблем может занять довольно много времени. Основной причиной обеих проблем является недоступность файла класса (в пути к классам) во время выполнения.

  1. We need to make sure whether class or jar containing that class is available in the classpath. If not, we need to add it
  2. If it’s available on application’s classpath then most probably classpath is getting overridden. To fix that we need to find the exact classpath used by our application
  3. Also, if an application is using multiple class loaders, classes loaded by one classloader may not be available by other class loaders. To troubleshoot it well, it’s essential to know how classloaders work in Java

Давайте рассмотрим несколько подходов, которые мы можем рассмотреть при работе с любым из них:

5. Резюме

Хотя оба эти исключения связаны с classpath и средой выполнения Java, которая не может найти класс во время выполнения, важно отметить их различия.

Среда выполнения Java генерирует исключение ClassNotFoundException при попытке загрузить класс только во время выполнения, а имя было предоставлено во время выполнения. В случае NoClassDefFoundError класс присутствовал во время компиляции, но среда выполнения Java не смогла найти его в пути к классам Java во время выполнения.