为什么我在Java中得到一个NoClassDefFoundError ?

当我运行Java应用程序时,我得到一个NoClassDefFoundError。造成这种情况的典型原因是什么?

1009119 次浏览

这是由于您的代码所依赖的类文件在编译时存在,但在运行时没有找到。寻找构建时和运行时类路径的差异。

我发现,当使用运行时发现的不兼容的类版本编译代码时,有时会得到NoClassDefFound错误。我记得的具体实例是apache axis库。在我的运行时类路径上实际上有2个版本,它正在挑选过时和不兼容的版本,而不是正确的版本,导致NoClassDefFound错误。这是在一个命令行应用程序中,我正在使用类似的命令。

set classpath=%classpath%;axis.jar

我能够让它拿起正确的版本使用:

set classpath=axis.jar;%classpath%;

虽然这可能是由于编译时和运行时之间的类路径不匹配造成的,但这并不一定是真的。

在这种情况下,记住两到三个不同的异常是很重要的:

  1. java.lang.ClassNotFoundException此异常表示在类路径中没有找到该类。这表明我们正在尝试加载类定义,而类路径上不存在类。

  2. 此异常表明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;
}

我使用Spring框架Maven,并在我的项目中解决了这个错误。

类中存在运行时错误。我将一个属性读取为整数,但是当它从属性文件读取值时,它的值是double。

Spring没有给我一个完整的堆栈跟踪运行时失败的行。 它只是说NoClassDefFoundError。但是当我作为一个本地Java应用程序执行它(将其从MVC中取出)时,它给出了ExceptionInInitializerError,这是真正的原因,也是我跟踪错误的方式

@xli的回答让我洞察到我的代码中可能存在的错误。

当运行时类装入器装入的类不能访问已经由java rootloader装入的类时,我得到NoClassFoundError。因为不同的类装入器在不同的安全域中(根据java), jvm不允许已经由rootloader装入的类在运行时装入器地址空间中被解析。

使用'java -javaagent:trace .jar[你的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;
}
});
}
}

这是我目前为止找到的最好的解决方案

假设我们有一个名为org.mypackage的包,其中包含以下类:

  • HelloWorld(主类)
  • SupportClass
  • UtilClass

定义这个包的文件物理存储在D:\myprogram (Windows)或/home/user/myprogram (Linux)目录下。

文件结构如下所示: # EYZ0 < / p > 当调用Java时,我们指定要运行的应用程序的名称:org.mypackage.HelloWorld。但是,我们还必须告诉Java在哪里查找定义包的文件和目录。所以要启动程序,我们必须使用下面的命令: # EYZ0 < / p >

我从SRC库中删除了两个文件后得到了这个消息,当我把它们带回来时,我一直看到这个错误消息。

我的解决方案是:重新启动Eclipse。从那以后我就再没见过这条消息了:-)

确保在module:appmodule:lib中匹配:

android {
compileSdkVersion 23
buildToolsVersion '22.0.1'
packagingOptions {
}


defaultConfig {
minSdkVersion 17
targetSdkVersion 23
versionCode 11
versionName "2.1"
}

我也遇到了同样的问题,我的存货积压了好几个小时。

我找到了解决办法。在我的例子中,有一个静态方法因此而定义。JVM不能创建该类的另一个对象。

例如,

private static HttpHost proxy = new HttpHost(proxyHost, Integer.valueOf(proxyPort), "http");

下面的技巧帮助了我很多次:

System.out.println(TheNoDefFoundClass.class.getProtectionDomain().getCodeSource().getLocation());

其中nodeffoundclass是可能会“丢失”的类,这是由于您的程序使用相同库的旧版本的首选项。当客户端软件被部署到一个占主导地位的容器中,并配备了自己的类加载器和大量最流行的库的古老版本时,这种情况最常发生。

NoClassDefFoundError在Java中

# EYZ0

  1. Java虚拟机无法在运行时找到在编译时可用的特定类。

  2. 如果一个类在编译时存在,但在运行时在java类路径中不可用。

enter image description here

例子:

  1. 类不在Classpath中,没有确定的方法知道它,但很多时候你可以看看打印System.getproperty("java.classpath"),它会从那里打印类路径,你至少可以了解你实际的运行时类路径。
  2. NoClassDefFoundError的一个简单的例子是类属于一个丢失的JAR文件,或者JAR没有添加到类路径中,或者有时JAR的名字被某人更改了,比如在我的情况下,我的一个同事已经将tibco.jar更改为tibco_v3.jar,程序失败了java.lang.NoClassDefFoundError,我想知道出了什么问题。

  3. 只需尝试显式地使用-classpath选项运行您认为将工作的类路径,如果它工作,那么这是一个肯定的短信号,有人重写java类路径。

  4. 在Java中,JAR文件的权限问题也会导致NoClassDefFoundError。
  5. 在Java中,XML配置上的拼写错误也会导致NoClassDefFoundError。
  6. 当你在包中定义的编译类在加载时没有出现在同一个包中,就像在JApplet的情况下,它会抛出Java中的NoClassDefFoundError。

可能的解决方式:

  1. 这个类在Java Classpath中不可用。
  2. 如果您在J2EE环境中工作,那么在多个Classloader之间的Class可见性也会导致java.lang. lang. net。NoClassDefFoundError,有关详细讨论,请参阅示例和场景部分。
  3. 在日志文件中检查java.lang.ExceptionInInitializerError。由于静态初始化失败而导致的NoClassDefFoundError是非常常见的。
  4. 因为NoClassDefFoundError是java.lang.LinkageError的子类,如果它的一个依赖项(如本机库)可能不可用,它也会出现。
  5. 任何启动脚本都重写Classpath环境变量。
  6. 您可能正在使用jar命令运行您的程序,并且class没有在清单文件的ClassPath属性中定义。

资源:

3种方法来解决NoClassDefFoundError

java.lang。问题模式

如果您有生成代码(EMF等),可能会有太多的静态初始化器,这会占用所有的堆栈空间。

参见Stack Overflow问题如何增加Java堆栈大小?< / >

如果有人因为java.lang.NoClassDefFoundError: org/apache/log4j/Logger错误来到这里,在我的例子中,它是因为我使用log4j 2(但我没有添加它所附带的所有文件)而产生的,并且一些依赖库使用log4j 1。解决方案是添加Log4j 1。x桥:log4j 2附带的jar log4j-1.2-api-<version>.jar。更多信息在log4j 2 迁移中。

同一项目的两个不同的签出副本

在我的案例中,问题在于Eclipse无法区分同一个项目的两个不同副本。我有一个锁定在中继(SVN版本控制),另一个工作在一个分支时间。我在工作副本中尝试了一个更改作为JUnit测试用例,其中包括将一个私有内部类单独提取为一个公共类,在它工作时,我打开项目的另一个副本,查看需要更改的代码的其他部分。在某个时刻,NoClassDefFoundError跳出来抱怨私有内部类不在那里;在堆栈跟踪中双击将我带到错误的项目副本中的源文件。

关闭项目的主干副本并再次运行测试用例可以解决这个问题。

Java无法在运行时找到类A。 Class A在maven项目ArtClient中,来自不同的工作空间。 因此,我将ArtClient导入到Eclipse项目中。 我的两个项目使用ArtClient作为依赖项。 我将这些库引用更改为项目引用(构建路径->配置构建路径)

问题就这样解决了。

我通过禁用所有模块的preDexLibraries来解决我的问题:

dexOptions {
preDexLibraries false
...

此错误可能是由未选中的Java版本需求引起的。

以我为例,我在构建一个备受瞩目的开源项目时,通过使用SDKMAN !从Java 9切换到Java 8,解决了这个错误。

sdk list java
sdk install java 8u152-zulu
sdk use java 8u152-zulu

然后按照下面的描述进行干净安装。


当使用Maven作为构建工具时,执行清洁“安装”构建禁用测试有时是有帮助的,而且通常是令人满意的。

mvn clean install -DskipTests

现在已经构建并安装了一切,您可以继续运行测试。

mvn test

NoClassDefFoundError也可能发生在静态初始化器试图加载一个在运行时不可用的资源包时,例如受影响的类试图从META-INF目录加载一个属性文件,但该目录不存在。如果您没有捕获NoClassDefFoundError,有时您将无法看到完整的堆栈跟踪;为了克服这个问题,你可以临时使用catch子句来代替Throwable:

try {
// Statement(s) that cause(s) the affected class to be loaded
} catch (Throwable t) {
Logger.getLogger("<logger-name>").info("Loading my class went wrong", t);
}

一个有趣的情况是,你可能会看到很多NoClassDefFoundErrors:

  1. Example类的static块中的throwRuntimeException
  2. 拦截它(或者如果它被扔在测试用例中并不重要)
  3. 尝试创建这个类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将与ExceptionInInitializerError一起从静态块RuntimeException抛出。


当您在单元测试中看到NoClassDefFoundErrors时,这一点尤其重要。

在某种程度上,您在测试之间“共享”static块执行,但是初始的ExceptionInInitializerError将只在一个测试用例中。第一个使用有问题的Example类。其他使用Example类的测试用例将只抛出NoClassDefFoundErrors

当我没有在项目的Java Build Path中的“Order and export”选项卡上导出一个类时,我得到了NoClassDefFound错误。确保在添加到项目构建路径的任何依赖项的“Order and Export”选项卡中添加一个复选标记。看到# EYZ0。

也可能是因为您从具有特定包名的IDE复制了代码文件,并且您想尝试使用终端运行它。您必须首先从代码中删除包名。

当我将另一个模块的Maven依赖添加到我的项目中时,我得到了这个错误,这个问题最终通过将-Xss2m添加到我的程序的JVM选项来解决(自JDK5.0以来默认是1m字节)。人们认为程序没有足够的堆栈来加载类。

在我的情况下,由于JDK版本不匹配,我得到了这个错误。当我试图从Intelij运行应用程序时,它不工作,但从命令行运行它就工作了。这是因为Intelij试图用Java 11 JDK运行它,但在命令行上,它是用Java 8 JDK运行的。在文件>项目结构>项目设置>项目SDK下切换设置后,它为我工作了。

每个人都在这里谈论一些Java配置的东西,JVM的问题等,在我的情况下,错误与这些主题完全无关,有一个非常琐碎和容易解决的原因:我在我的控制器(Spring Boot应用程序)的端点有一个错误的注释。

我有一个有趣的问题与NoClassDefFoundError在JavaEE工作与自由服务器。我正在使用IMS资源适配器,我的server.xml已经有了imsudbJXA.rar的资源适配器。 当我为imsudbXA.rar添加新的适配器时,我将开始为DLIException, IMSConnectionSpec或SQLInteractionSpec的实例对象获得此错误。 我不知道为什么,但是我只用imsudbXA.rar为我的工作创建了新的server.xml来解决这个问题。我相信在server.xml中使用多个资源适配器是可以的,我只是没有时间去研究它

更新[https://www.infoq.com/articles/single-file-execution-java11/]:

在Java SE 11中,您可以选择启动单个源代码文件 直接,不需要中间编译。为了方便大家, 所以像你这样的新手不需要运行javac + Java(当然, 让他们感到困惑)。

我有这个错误,但无法根据这个线程找出解决方案,但我自己解决了。

对于我的问题,我正在编译以下代码:

package valentines;


import java.math.BigInteger;
import java.util.ArrayList;


public class StudentSolver {
public static ArrayList<Boolean> solve(ArrayList<ArrayList<BigInteger>> problems) {
//DOING WORK HERE
        

}
public static void main(String[] args){
//TESTING SOLVE FUNCTION
}
    

}
我然后在/ProjectName/valentines这样的文件夹结构中编译这段代码 编译它工作得很好,但试图执行:java StudentSolver

我得到了NoClassDefError。

为了解决这个问题,我只是删除了:package valentines;

我不是很精通java包等,但这是我如何修复我的错误,非常抱歉,如果这已经由其他人回答,但我无法解释它到我的问题。

当我试图在Tomcat/JBOSS服务器上部署应用程序时,得到了NoClassDefFoundError。我尝试了不同的依赖关系来解决这个问题,但总是得到相同的错误。标记所有javax。*依赖提供在pom.xml,和战争字面上没有依赖。但这个问题还是不断出现。

最后意识到src / main / webapps / web - inf / <强> < / >强类文件夹,它被复制到我的war中,所以不是编译类,而是这个类被复制,因此没有依赖改变解决问题。

因此要小心,如果任何先前编译的数据被复制,删除类文件夹和新的编译后,它工作!..

Java ClassNotFoundException vs NoClassDefFoundError

< a href = " https://stackoverflow.com/a/66815988/4770877 " > < / >(类加载器)

静态与动态类加载

Static(Implicit) class loading -引用、实例化或继承的结果。

MyClass myClass = new MyClass();

Dynamic(Explicit) class loading是Class.forName(), loadClass(), findSystemClass()的结果

MyClass myClass = (MyClass) Class.forName("MyClass").newInstance();

每个类都有一个使用loadClass(String name);ClassLoader,这就是为什么

explicit class loader uses implicit class loader

NoClassDefFoundErrorexplicit class loader的一部分。使用Error来保证在编译期间出现这个类,但现在(在运行时)它不存在。

ClassNotFoundExceptionimplicit class loader的一部分。Exception对于可以使用它的场景是有弹性的——例如反射。

我对此的解决办法是“有效”;丢失的特定类的类路径内容。在我的情况下,我有2个依赖项,虽然我能够成功地编译使用javac…,我不能运行产生的类文件使用java…,因为BouncyCastle jar中的Dynamic类不能在运行时加载。

javac --classpath "ext/commons-io-2.11.0;ext/bc-fips-1.0.2.3" hello.java

因此在编译时和运行时,JVM知道在哪里获取Apache Commons和BouncyCastle依赖项,然而,当运行这个时,我得到了

Error: Unable to initialize main class hello
Caused by: java.lang.NoClassDefFoundError:
org/bouncycastle/jcajce/provider/BouncyCastleFipsProvider

因此,根据类路径,我在相同的位置手动创建了一个名为ext的新文件夹,然后在其中放置了BouncyCastle jar,以确保在运行时可以找到它。只要结果清单中指定了jar的位置,您就可以将jar相对于类文件或jar文件放置。注意,我只需要利用包含丢失类文件的一个jar。