Ответ 1
Это вызвано тем, что есть файл класса, от которого зависит ваш код, и он присутствует во время компиляции, но не найден во время выполнения. Ищите различия в времени сборки и классах времени выполнения.
Я получаю NoClassDefFoundError
, когда запускаю приложение Java. Что обычно является причиной этого?
Это вызвано тем, что есть файл класса, от которого зависит ваш код, и он присутствует во время компиляции, но не найден во время выполнения. Ищите различия в времени сборки и классах времени выполнения.
Хотя возможно, что это связано с несоответствием пути к классам между временем компиляции и временем выполнения, это не обязательно верно.
Важно сохранить два или три разных исключения прямо в нашей голове в этом случае:
java.lang.ClassNotFoundException
Это исключение указывает, что класс не найден в пути к классам. Это указывает на то, что мы пытались загрузить определение класса, и класс не существовал в пути к классам.
java.lang.NoClassDefFoundError
Это исключение указывает на то, что JVM просмотрел внутреннюю структуру данных определения класса для определения класса и не нашел его. Это отличается от того, что он не может быть загружен из пути к классам. Обычно это указывает на то, что мы ранее пытались загрузить класс из пути к классам, но по какой-то причине он потерпел неудачу - теперь мы снова пытаемся использовать этот класс (и, следовательно, необходимо загрузить его, поскольку он провалился в последний раз) я даже не попытаюсь загрузить его, потому что мы не выполнили его загрузку ранее (и разумно подозреваем, что мы снова потерпим неудачу). Ранним отказом может быть исключение ClassNotFoundException или ExceptionInInitializerError (указывающее на отказ в статическом блоке инициализации) или любое количество других проблем. Дело в том, что NoClassDefFoundError не обязательно является проблемой класса.
Вот код для иллюстрации java.lang.NoClassDefFoundError
. Пожалуйста, обратитесь к Джареду за подробным объяснением.
NoClassDefFoundErrorDemo.java
public class NoClassDefFoundErrorDemo {
public static void main(String[] args) {
try {
// The following line would throw ExceptionInInitializerError
SimpleCalculator calculator1 = new SimpleCalculator();
} catch (Throwable t) {
System.out.println(t);
}
// The following line would cause NoClassDefFoundError
SimpleCalculator calculator2 = new SimpleCalculator();
}
}
SimpleCalculator.java
public class SimpleCalculator {
static int undefined = 1 / 0;
}
NoClassDefFoundError В Java
Определение:
Java Virtual Machine не может найти определенный класс во время выполнения, который был доступен во время компиляции.
Если во время компиляции класс присутствовал, но не доступен в java classpath во время выполнения.
<сильные > Примеры:
Простым примером NoClassDefFoundError является класс, принадлежащий отсутствующему файлу JAR, или JAR не был добавлен в classpath, или иногда имя jar было изменено кем-то, например, в моем случае один из моих коллег изменил tibco.var на tibco_v3.jar и программа не работает с java.lang.NoClassDefFoundError, и мне было интересно, что не так.
Просто попробуйте запустить с явно параметром -classpath с классом classpath, который, по вашему мнению, будет работать, и если он работает, то это верный короткий знак того, что кто-то переопределяет путь класса Java.
Возможные решения:
Ресурсы
Я обнаружил, что иногда я получаю ошибку NoClassDefFound, когда код компилируется с несовместимой версией класса, найденного во время выполнения. Конкретный экземпляр, о котором я помню, связан с библиотекой осей apache. Фактически на моем пути к классам среды исполнения было 2 версии, и она собирала устаревшую и несовместимую версию, а не правильную, вызывая ошибку NoClassDefFound. Это было в приложении командной строки, где я использовал команду, подобную этой.
set classpath=%classpath%;axis.jar
Я смог заставить его подобрать подходящую версию, используя:
set classpath=axis.jar;%classpath%;
Это лучшее решение, которое я нашел до сих пор.
Предположим, у нас есть пакет с именем org.mypackage
содержащий классы:
и файлы, определяющие этот пакет, физически хранятся в каталоге D:\myprogram
(в Windows) или /home/user/myprogram
(в Linux).
Структура файла будет выглядеть так:
Когда мы вызываем Java, мы указываем имя приложения для запуска: org.mypackage.HelloWorld
. Однако мы также должны указать Java, где искать файлы и каталоги, определяющие наш пакет. Итак, чтобы запустить программу, мы должны использовать следующую команду:
Я использовал Spring Framework с Maven и решил эту ошибку в своем проекте.
В классе произошла ошибка времени выполнения. Я читал свойство как целое, но когда он считывал значение из файла свойств, его значение было двойным.
Spring не дал мне полную трассировку стека, на которой строка времени выполнения не удалась. Он просто сказал NoClassDefFoundError
. Но когда я выполнил его как родное Java-приложение (взяв его из MVC), он дал ExceptionInInitializerError
который был истинной причиной, и именно так я отслеживаю ошибку.
Ответ @xli дал мне представление о том, что может быть неправильным в моем коде.
Я получаю NoClassFoundError, когда классы, загруженные загрузчиком классов времени выполнения, не могут обращаться к классам, уже загруженным корневым загрузчиком java. Поскольку разные загрузчики классов находятся в разных доменах безопасности (в соответствии с java), jvm не разрешит классы, уже загруженные корневым загрузчиком, которые будут разрешены в адресном пространстве загрузчика времени выполнения.
Запустите свою программу с помощью java -javaagent: tracer.jar [YOUR java ARGS] '
Он производит вывод, показывающий загруженный класс, и загрузчик env, который загружал класс. Очень полезно отслеживать, почему класс не может быть разрешен.
// ClassLoaderTracer.java
// From: 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 + " loaded by " + loader + " at " + new java.util.Date() + " in " + pd);
// dump stack trace of the thread loading class
Thread.dumpStack();
// we just want the original .class bytes to be loaded!
// we are not instrumenting it...
return null;
}
});
}
}
Один интересный случай, в котором вы можете увидеть много NoClassDefFoundErrors
это когда вы:
throw
RuntimeException
в static
блок вашего класса. Example
Example
static class Example {
static {
thisThrowsRuntimeException();
}
}
static class OuterClazz {
OuterClazz() {
try {
new Example();
} catch (Throwable ignored) { //simulating catching RuntimeException from static block
// DO NOT DO THIS IN PRODUCTION CODE, THIS IS JUST AN EXAMPLE in StackOverflow
}
new Example(); //this throws NoClassDefFoundError
}
}
NoClassDefError
будет NoClassDefError
вместе с ExceptionInInitializerError
из статического блока RuntimeException
.
Это особенно важный случай, когда вы видите NoClassDefFoundErrors
в ваших UNIT TESTS.
В некотором смысле вы "разделяете" выполнение static
блока между тестами, но первоначальный ExceptionInInitializerError
будет только в одном тестовом случае. Первый, который использует проблемный класс Example
. Другие тестовые случаи, использующие класс Example
, просто NoClassDefFoundErrors
.
Если у вас есть сгенерированный код (EMF и т.д.), Может быть слишком много статических инициализаторов, которые потребляют все пространство стека.
См. Вопрос о переполнении стека. Как увеличить размер стека Java? ,
Техника ниже помогла мне много раз:
System.out.println(TheNoDefFoundClass.class.getProtectionDomain().getCodeSource().getLocation());
где TheNoDefFoundClass - это класс, который может быть "потерян" из-за предпочтения старой версии той же библиотеки, используемой вашей программой. Это чаще всего случается с случаями, когда клиентское программное обеспечение развертывается в доминирующем контейнере, вооружившись его собственными загрузчиками классов и множеством древних версий самых популярных библиотек.
Я исправил свою проблему, отключив preDexLibraries для всех модулей:
dexOptions {
preDexLibraries false
...
NoClassDefFoundError
также может возникать, когда статический инициализатор пытается загрузить пакет ресурсов, который недоступен во время выполнения, например файл свойств, который уязвимый класс пытается загрузить из каталога META-INF
, но его там нет. Если вы не перехватите NoClassDefFoundError
, иногда вы не сможете увидеть полную трассировку стека; чтобы преодолеть это, вы можете временно использовать условие catch
для Throwable
:
try {
// Statement(s) that cause the affected class to be loaded
} catch (Throwable t) {
Logger.getLogger("<logger-name>").info("Loading my class went wrong", t);
}
Если кто-то приходит сюда из-за ошибки java.lang.NoClassDefFoundError: org/apache/log4j/Logger
, в моем случае это было произведено, потому что я использовал log4j 2 (но я не добавлял все файлы, которые приходят с ним), а в некоторой библиотеке зависимостей используется log4j 1. решение заключалось в том, чтобы добавить мост Log4j 1.x: jar log4j-1.2-api-<version>.jar
, который поставляется с log4j 2. Дополнительная информация в log4j 2 migration.
В моем случае проблема заключалась в неспособности Eclipse различать две разные копии одного и того же проекта. У меня есть одна блокировка на багажнике (контроль версий SVN), а другой - в одной ветки за раз. Я опробовал одно изменение в рабочей копии как тестовый пример JUnit, который включал в себя извлечение частного внутреннего класса для самостоятельного публичного класса, и пока он работал, я открываю другую копию проекта, чтобы посмотреть вокруг на другой часть кода, которая нуждается в изменениях. В какой-то момент NoClassDefFoundError
появился, жалуясь, что частного внутреннего класса не было; двойной щелчок в трассировке стека привел меня к исходному файлу в неправильной копии проекта.
Закрытие соединительной копии проекта и запуск тестового примера снова избавилось от проблемы.
Эта ошибка может быть вызвана неконтролируемыми требованиями к Java-версии.
В моем случае я смог разрешить эту ошибку, создав высокопрофильный проект с открытым исходным кодом, переключившись с Java 9 на Java 8 с помощью SDKMAN! ,
sdk list java
sdk install java 8u152-zulu
sdk use java 8u152-zulu
Затем выполните чистую установку, как описано ниже.
При использовании Maven в качестве инструмента сборки иногда полезно - и, как правило, приятно, сделать чистую сборку "установка" с отключенным тестированием.
mvn clean install -DskipTests
Теперь, когда все было построено и установлено, вы можете продолжить тесты.
mvn test
Я получил ошибки NoClassDefFound, когда не экспортировал класс на вкладке "Порядок и экспорт" в пути сборки Java моего проекта. Обязательно поставьте галочку на вкладке "Заказ и экспорт" всех зависимостей, которые вы добавляете в путь сборки проекта. См. Предупреждение Eclipse: XXXXXXXXXXX.jar не будет экспортирован или опубликован. Runtime ClassNotFoundExceptions может привести.
Это также может быть связано с тем, что вы копируете файл кода из IDE с определенным именем пакета и хотите попробовать запустить его с помощью терминала. Сначала вам придется удалить имя пакета из кода. Это происходит со мной.
У меня была та же проблема, и я был запас в течение многих часов.
Я нашел решение. В моем случае из-за этого был установлен статический метод. JVM не может создать другой объект этого класса.
Например,
private static HttpHost proxy = new HttpHost(proxyHost, Integer.valueOf(proxyPort), "http");
Java не смог найти класс A во время выполнения. Класс A был в проекте Artline из maven из другого рабочего пространства. Поэтому я импортировал ArtClient в проект Eclipse. Два из моих проектов использовали ArtClient в качестве зависимости. Я изменил ссылку на библиотеку для ссылки на проект для этих (Путь сборки → Настроить путь сборки).
И проблема исчезла.
Я получил это сообщение после удаления двух файлов из библиотеки SRC, и когда я вернул их, я продолжал видеть это сообщение об ошибке.
Моим решением было: Перезапустить Eclipse. С тех пор я еще не видел этого сообщения :-)
Убедитесь, что это соответствует module:app
и module:lib
:
android {
compileSdkVersion 23
buildToolsVersion '22.0.1'
packagingOptions {
}
defaultConfig {
minSdkVersion 17
targetSdkVersion 23
versionCode 11
versionName "2.1"
}