运行Java应用程序时得到NoClassDefFoundError。 通常是什么原因造成的?
尽管这可能是由于编译时和运行时之间的类路径不匹配导致的,但不一定是正确的。
在这种情况下,请务必牢记两个或三个不同的异常:
java.lang.ClassNotFoundException此异常表明在类路径上找不到该类。这表明我们正在尝试加载类定义,并且该类在类路径中不存在。
java.lang.NoClassDefFoundError此异常表明JVM在其内部类定义数据结构中查找了类的定义,但未找到。这与说无法从类路径中加载不同。通常,这表明我们以前曾尝试从类路径中加载类,但由于某种原因而失败-现在我们正尝试再次使用该类(由于上次失败,因此需要对其进行加载),但是我们甚至不打算尝试加载它,因为我们无法更早加载它(并且合理地怀疑我们会再次失败)。较早的故障可能是ClassNotFoundException或ExceptionInInitializerError(指示静态初始化块中的故障)或许多其他问题。关键是,NoClassDefFoundError不一定是类路径问题。
当您的代码依赖于一个类文件,并且该类文件在编译时存在但在运行时未找到时,会导致这种情况。在构建时间和运行时类路径中寻找差异。
这是说明java.lang.NoClassDefFoundError的代码。请参阅Jared的答案以获取详细说明。
NoClassDefFoundErrorDemo.java
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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
1 2 3
| public class SimpleCalculator {
static int undefined = 1 / 0;
} |
Java中的NoClassDefFoundError
定义:
Java虚拟机无法在运行时找到编译时可用的特定类。
如果一个类在编译期间存在,但在运行时在java classpath中不可用。
例子:
该类不在Classpath中,没有确定的了解方法,但是很多时候您只需要看一下即可打印System.getproperty(" java.classpath"),它将从那里打印出类路径,您至少可以得到您的实际运行时类路径的想法。
NoClassDefFoundError的一个简单示例是类属于丢失的JAR文件,或者未将JAR添加到类路径中,或者有时有人更改了jar的名称,例如我的同事之一将tibco.jar更改为tibco_v3.jar,程序是java.lang.NoClassDefFoundError失败,我想知道怎么了。
只需尝试在您认为可以使用的类路径上显式地使用-classpath选项即可运行,如果可以正常运行,那么这肯定是有人重写Java类路径的简短标志。
JAR文件的权限问题也可能导致Java中出现NoClassDefFoundError。
XML配置上的错字也可能导致Java中的NoClassDefFoundError。
当在包中定义的已编译类在加载时不在同一个包中时(如JApplet),它将在Java中引发NoClassDefFoundError。
可能的解决方案:
该类在Java Classpath中不可用。
如果您在J2EE环境中工作,则多个Classloader之间Class的可见性也可能导致java.lang.NoClassDefFoundError,请参阅示例和场景一节以获取详细讨论。
在日志文件中检查java.lang.ExceptionInInitializerError。由于静态初始化失败而导致的NoClassDefFoundError非常普遍。
由于NoClassDefFoundError是java.lang.LinkageError的子类,因此如果其中一个依赖项(如本机库)可能不可用,它也可能会出现。
任何启动脚本都将覆盖Classpath环境变量。
您可能正在使用jar命令运行程序,并且清单文件的ClassPath属性中未定义类。
资源:
解决NoClassDefFoundError的3种方法
java.lang.NoClassDefFoundError问题模式
我发现有时使用在运行时发现的类的不兼容版本编译代码时,有时会出现NoClassDefFound错误。我记得的特定实例与apache轴库有关。我的运行时类路径上实际上有2个版本,并且它选择的是过时且不兼容的版本,而不是正确的版本,从而导致NoClassDefFound错误。这是在命令行应用程序中,我在其中使用与此类似的命令。
1
| set classpath=%classpath%;axis.jar |
我能够使用以下命令获取正确的版本:
1
| set classpath=axis.jar;%classpath%; |
这是我到目前为止发现的最佳解决方案。
假设我们有一个名为org.mypackage的包,其中包含以下类:
-
HelloWorld(主类)
-
SupportClass
-
UtilClass
并且定义此软件包的文件实际存储在目录D:\myprogram(在Windows上)或/home/user/myprogram(在Linux上)下。
文件结构将如下所示:
调用Java时,我们指定要运行的应用程序的名称:org.mypackage.HelloWorld。但是,我们还必须告诉Java在哪里寻找定义我们程序包的文件和目录。因此,要启动该程序,我们必须使用以下命令:
我在Maven中使用Spring Framework,并在我的项目中解决了此错误。
该类中存在运行时错误。我正在将属性读取为整数,但是当它从属性文件读取值时,其值是两倍。
Spring并没有给我完整的堆栈跟踪信息,表明运行失败的那一行。
它只是说NoClassDefFoundError。但是,当我将其作为本机Java应用程序(从MVC中取出)执行时,它给出了ExceptionInInitializerError的真正原因,这也是我跟踪错误的方式。
@xli的答案使我深入了解了我的代码中可能存在的问题。
当运行时类加载器加载的类无法访问Java rootloader已加载的类时,出现NoClassFoundError。由于不同的类加载器位于不同的安全域中(根据Java),因此jvm不允许在运行时加载器地址空间中解析由根加载器加载的类。
使用" java -javaagent:tracer.jar [您的Java插件]"运行程序
它产生的输出显示已加载的类,以及加载该类的加载器env。跟踪为什么无法解析类非常有用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| // 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;
}
});
}
} |
尤其是如果您在UNIT TESTS中看到
NoClassDefFoundErrors,请仔细阅读...
您可能会看到很多NoClassDefFoundErrors的有趣情况是:
类Example的static块中的throw和RuntimeException
拦截它(或者就像在测试用例中抛出一样没关系)
尝试创建此类Example的实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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将与ExceptionInInitializerError一起从静态块RuntimeException中引发。
当您在UNIT TESTS中看到NoClassDefFoundErrors时,这尤其重要。
在某种程度上,您是在测试之间"共享" static块的执行,但是初始的ExceptionInInitializerError只是在一个测试用例中。第一个使用有问题的Example类。使用Example类的其他测试用例只会抛出NoClassDefFoundErrors。
如果您已生成代码(EMF等),则可能有太多静态初始化程序占用所有堆栈空间。
请参见堆栈;溢出问题;如何增加Java堆栈大小?
以下技术对我有很多帮助:
1
| System.out.println(TheNoDefFoundClass.class.getProtectionDomain().getCodeSource().getLocation()); |
其中,TheNoDefFoundClass是由于对程序使用的同一个库的较旧版本的偏好而可能"丢失"的类。这种情况最常发生在以下情况中:将客户端软件部署到一个占主导地位的容器中,并带有其自己的类加载器和成千上万个最流行的lib的古代版本。
我通过禁用所有模块的preDexLibraries解决了我的问题:
1 2 3
| dexOptions {
preDexLibraries false
... |
当静态初始化程序尝试加载运行时不可用的资源束时,例如也会出现NoClassDefFoundError,受影响的类尝试从META-INF目录加载但不存在的属性文件。如果您没有捕获NoClassDefFoundError,有时您将看不到完整的堆栈跟踪信息。为了克服这个问题,您可以为Throwable临时使用catch子句:
1 2 3 4 5
| try {
// Statement(s) that cause the affected class to be loaded
} catch (Throwable t) {
Logger.getLogger(";lt;logger-name;gt;").info("Loading my class went wrong", t);
} |
也可能是因为您从IDE复制具有特定程序包名称的代码文件,并想尝试使用终端运行它。您必须首先从代码中删除程序包名称。
这发生在我身上。
如果有人是因为java.lang.NoClassDefFoundError: org/apache/log4j/Logger错误而来这里的,那么在我的情况下,它是由于我使用log4j 2而产生的(但是我没有添加它随附的所有文件),而某些依赖库使用了log4j 1。添加Log4j 1.x桥:log4j 2随附的jar log4j-1.2-api-;lt;version;gt;.jar。有关log4j 2迁移的更多信息。
同一项目的两个不同的结帐副本
就我而言,问题是Eclipse无法区分同一项目的两个不同副本。我有一个锁定在主干(SVN版本控制)上,而另一个则一次在一个分支中工作。我尝试将工作副本中的一个更改作为JUnit测试用例进行了测试,其中包括将一个私有内部类单独提取为一个公共类,并且在工作时,我打开了该项目的另一个副本以查看其他内容。需要更改的部分代码。在某个时候,NoClassDefFoundError突然弹出,抱怨私有内部类不存在。双击堆栈跟踪将我带到错误的项目副本中的源文件。
关闭项目的主干副本并再次运行测试用例解决了这个问题。
此错误可能是由于未经检查的Java版本要求引起的。
就我而言,通过使用SDKMAN!从Java 9切换到Java 8,在构建备受瞩目的开源项目时,我能够解决此错误。
1 2 3
| sdk list java
sdk install java 8u152-zulu
sdk use java 8u152-zulu |
然后按照以下说明进行全新安装。
当使用Maven作为构建工具时,有时会很有帮助-通常令人欣喜的是,在禁用测试的情况下进行全新的"安装"构建。
1
| mvn clean install -DskipTests |
现在,所有内容均已构建并安装,您可以继续运行测试。
当我没有在项目的Java Build Path的" Order and Export"选项卡上导出类时,出现NoClassDefFound错误。确保在添加到项目的构建路径中的所有依赖项的"订单和导出"选项卡中打上对勾。请参阅Eclipse警告:XXXXXXXXXXX.jar将不会导出或发布。运行时ClassNotFoundExceptions可能会导致。
Java无法在运行时中找到类A。
A类在其他工作区的Maven项目ArtClient中。
因此,我将ArtClient导入了我的Eclipse项目。
我的两个项目使用ArtClient作为依赖项。
我将库参考更改为这些项目的项目参考(构建路径->配置构建路径)。
问题消失了。
我遇到了同样的问题,而且库存很多小时。
我找到了解决方案。在我的情况下,因此定义了静态方法。 JVM无法创建该类的另一个对象。
例如,
1
| private static HttpHost proxy = new HttpHost(proxyHost, Integer.valueOf(proxyPort),"http"); |
从SRC库中删除了两个文件后,我收到了此消息,当我将它们放回时,我一直看到此错误消息。
我的解决方案是:重新启动Eclipse。从那时起,我再也没有看到此消息:-)
确保在module:app和module:lib中匹配:
1 2 3 4 5 6 7 8 9 10 11 12
| android {
compileSdkVersion 23
buildToolsVersion '22.0.1'
packagingOptions {
}
defaultConfig {
minSdkVersion 17
targetSdkVersion 23
versionCode 11
versionName"2.1"
} |