Почему возникает ошибка NoClassDefFoundError в Java?
Я получаю ошибку NoClassDefFoundError
при запуске моего Java-приложения. В чем обычно заключается причина этой ошибки?
5 ответ(ов)
Это может быть связано с несовпадением classpath во время компиляции и выполнения, но не обязательно.
Важно четко различать две или три разные исключения в этом случае:
java.lang.ClassNotFoundException
- это исключение указывает на то, что класс не был найден в classpath. Это означает, что мы пытались загрузить определение класса, но его не оказалось в classpath.java.lang.NoClassDefFoundError
- это исключение указывает на то, что JVM искала определение класса в своей внутренней структуре данных, но не нашла его. Это отличается от ситуации, когда класс не может быть загружен из classpath. Обычно это указывает на то, что мы раньше пытались загрузить класс из classpath, но по какой-то причине это не удалось - теперь мы пытаемся снова использовать этот класс (и следовательно, нам нужно его загрузить, так как это не вышло в прошлый раз), но мы даже не собираемся пытаться его загрузить, потому что уже не удалось загрузить его ранее (и разумно подозреваем, что мы снова потерпим неудачу). Предыдущая ошибка может быть какClassNotFoundException
, так иExceptionInInitializerError
(указывает на сбой в статическом блоке инициализации) или может быть вызвана множеством других проблем. Главное, чтоNoClassDefFoundError
не обязательно является проблемой с classpath.
Это происходит, когда существует файл класса, от которого зависит ваш код, и он присутствует во время компиляции, но не найден во время выполнения. Проверьте различия в classpath для времени сборки и времени выполнения.
Ошибка NoClassDefFound может возникать, когда код компилируется с использованием несовместимой версии класса, которая затем загружается во время выполнения. В моем случае это произошло при использовании библиотеки Apache Axis. У меня в пути классов было две версии этой библиотеки, и приложение подхватывало устаревшую и несовместимую версию, что и вызывало ошибку NoClassDefFound.
Это произошло в приложении командной строки, и я использовал подобную команду для установки пути классов:
set classpath=%classpath%;axis.jar
Чтобы устранить проблему и заставить приложение использовать правильную версию библиотеки, я поменял порядок в classpath
следующим образом:
set classpath=axis.jar;%classpath%;
Теперь все работает корректно, и ошибка больше не возникает.
Одним из интересных случаев, когда вы можете столкнуться с множеством ошибок NoClassDefFoundError
, является ситуация, когда вы:
throw
'итеRuntimeException
вstatic
блоке вашего классаExample
- Перехватываете это исключение (или если это вас не беспокоит, как в случае с тестами)
- Пытаетесь создать экземпляр этого класса
Example
static class Example {
static {
thisThrowsRuntimeException();
}
}
static class OuterClazz {
OuterClazz() {
try {
new Example();
} catch (Throwable ignored) { //симуляция перехвата RuntimeException из static блока
// НИКОГДА НЕ ДЕЛАЙТЕ ЭТО В ПРОДАКШН-КОДЕ, ЭТО ТОЛЬКО ПРИМЕР для StackOverflow
}
new Example(); //это приведет к NoClassDefFoundError
}
}
В данном случае NoClassDefError
будет выброшено вместе с ExceptionInInitializerError
из-за RuntimeException
в статическом блоке.
Это особенно важный случай, когда вы сталкиваетесь с NoClassDefFoundErrors
в ваших ЮНИТ-ТЕСТАХ.
В некотором смысле вы "делите" выполнение static
блока между тестами, но исходная ExceptionInInitializerError
будет только в одном тесте — в том, который использует проблемный класс Example
. Другие тесты, которые также используют класс Example
, будут просто выбрасывать NoClassDefFoundErrors
.
Ошибка NoClassDefFoundError
возникает, когда классы, загруженные классовым загрузчиком времени выполнения, не могут получить доступ к классам, уже загруженным корневым загрузчиком (root loader) Java. Поскольку различные загрузчики классов находятся в разных доменах безопасности (по определению Java), JVM не позволяет классам, уже загруженным корневым загрузчиком, быть разрешенными в пространстве адресов загрузчика времени выполнения.
Чтобы лучше понять, почему класс не может быть разрешен, вы можете использовать следующий подход: запустите вашу программу с помощью команды
java -javaagent:tracer.jar [ВАШИ java ПАРАМЕТРЫ]
Это произведет вывод, показывающий загруженные классы и загрузчик, который их загрузил. Это очень полезно для отслеживания причин, по которым класс не может быть разрешен.
Вот пример кода для ClassLoaderTracer.java
, который можно использовать для трейсинга загрузки классов:
// ClassLoaderTracer.java
// Из: https://blogs.oracle.com/sundararajan/entry/tracing_class_loading_1_5
import java.lang.instrument.*;
import java.security.*;
// manifest.mf
// Premain-Class: ClassLoadTracer
// jar -cvfm tracer.jar manifest.mf ClassLoaderTracer.class
// java -javaagent:tracer.jar [...]
public class ClassLoadTracer
{
public static void premain(String agentArgs, Instrumentation inst)
{
final java.io.PrintStream out = System.out;
inst.addTransformer(new ClassFileTransformer() {
public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
String pd = (null == protectionDomain) ? "null" : protectionDomain.getCodeSource().toString();
out.println(className + " загружен " + loader + " в " + new java.util.Date() + " в " + pd);
// вывод стека вызовов для потока, загружающего класс
Thread.dumpStack();
// мы просто хотим, чтобы оригинальные .class байты были загружены!
// мы не модифицируем их...
return null;
}
});
}
}
Используйте этот код, чтобы добавить отслеживание и разобраться с проблемами загрузки классов в вашем приложении.
Инициализация ArrayList в одну строчку
Что значит "Не удалось найти или загрузить основной класс"?
Почему в RecyclerView отсутствует onItemClickListener()?
Что значит 'synchronized'?
Почему нет ConcurrentHashSet, если есть ConcurrentHashMap?