繁体   English   中英

为什么我在 Java 中收到 NoClassDefFoundError?

[英]Why am I getting a NoClassDefFoundError in Java?

我在运行 Java 应用程序时收到NoClassDefFoundError 这通常是什么原因造成的?

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

在这种情况下,将两个或三个不同的例外直接放在我们的脑海中是很重要的:

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

  2. java.lang.NoClassDefFoundError该异常表示JVM在其内部类定义数据结构中查找类的定义,但没有找到。 这与说它无法从类路径加载不同。 通常这表明我们之前尝试从类路径加载一个类,但由于某种原因它失败了 - 现在我们试图再次使用该类(因此需要加载它,因为它上次失败了),但是我们'甚至不会尝试加载它,因为我们之前未能加载它(并且有理由怀疑我们会再次失败)。 较早的失败可能是 ClassNotFoundException 或 ExceptionInInitializerError(指示静态初始化块中的失败)或任何数量的其他问题。 关键是,NoClassDefFoundError 不一定是类路径问题。

当您的代码依赖于一个类文件并且它在编译时存在但在运行时找不到时会导致这种情况。 寻找构建时间和运行时类路径的差异。

这是说明java.lang.NoClassDefFoundError的代码。 有关详细说明,请参阅Jared 的回答

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();
    }

}

简单计算器.java

public class SimpleCalculator {
    static int undefined = 1 / 0;
}

Java中的NoClassDefFoundError

定义:

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

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

在此处输入图像描述

例子:

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

  3. 只需尝试使用您认为可以工作的类路径显式运行 -classpath 选项,如果它正在工作,那么这是一个肯定的简短信号,表明有人正在覆盖 java 类路径。

  4. JAR 文件的权限问题也可能导致 Java 中的 NoClassDefFoundError。
  5. XML 配置上的错字也可能导致 Java 中的 NoClassDefFoundError。
  6. 当您在包中定义的编译类在加载时不存在于同一个包中时,就像在 JApplet 的情况下一样,它将在 Java 中抛出 NoClassDefFoundError。

可能的解决方案:

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

资源:

解决 NoClassDefFoundError 的 3 种方法

java.lang.NoClassDefFoundError 问题模式

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

set classpath=%classpath%;axis.jar

我能够通过使用以下方法获取正确的版本:

set classpath=axis.jar;%classpath%;

这是迄今为止我找到的最好的解决方案

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

  • HelloWorld(主类)
  • 支持类
  • 实用程序类

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

文件结构将如下所示: 在此处输入图像描述

当我们调用 Java 时,我们指定要运行的应用程序的名称: org.mypackage.HelloWorld 然而,我们还必须告诉 Java 在哪里寻找定义我们包的文件和目录。 因此,要启动程序,我们必须使用以下命令: 在此处输入图像描述

您可能会看到很多NoClassDefFoundErrors的一个有趣案例是:

  1. 在类Examplestatic块中throw RuntimeException
  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将与静态块RuntimeException中的ExceptionInInitializerError一起抛出。


当您在UNIT TESTS中看到NoClassDefFoundErrors时,这一点尤其重要。

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

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

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

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

@xli 的回答让我深入了解了我的代码中可能存在的问题。

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

使用“java -javaagent:tracer.jar [YOUR java ARGS]”运行程序

它生成显示加载的类的输出,以及加载该类的加载器环境。 跟踪类无法解析的原因非常有帮助。

// 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;
            }
        });
    }
}

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

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

其中 TheNoDefFoundClass 是由于偏爱程序使用的同一库的旧版本而可能“丢失”的类。 这最常发生在这样的情况下,当客户端软件被部署到一个占主导地位的容器中时,它配备了自己的类加载器和大量最流行的库的古老版本。

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

请参阅堆栈溢出问题如何增加 Java 堆栈大小? .

ClassNotFoundException 与 NoClassDefFoundError

[类加载器]

静态与动态类加载

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 - 例如反射。

同一项目的两个不同的结帐副本

就我而言,问题在于 Eclipse 无法区分同一项目的两个不同副本。 我有一个锁定在主干上(SVN 版本控制),另一个在一个分支中工作。 我在工作副本中尝试了一个更改作为 JUnit 测试用例,其中包括将私有内部类提取为自己的公共类,当它工作时,我打开项目的另一个副本以查看其他的需要更改的部分代码。 在某些时候,弹出NoClassDefFoundError抱怨私有内部类不存在; 双击堆栈跟踪将我带到错误项目副本中的源文件。

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

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

dexOptions {
        preDexLibraries false
        ...

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

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);
}

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

尝试在 Tomcat/JBOSS 服务器上部署应用程序时遇到NoClassDefFoundError 我使用不同的依赖项来解决问题,但不断收到相同的错误。 将所有 javax.* 依赖项标记为 pom.xml 中提供的,并且 war 实际上没有依赖项。 问题还是不断出现。

终于意识到src/main/webapps/WEB-INF/ classesclasses文件夹,该文件夹被复制到我的 war 中,因此这些类不是编译的类,而是被复制,因此没有依赖项更改解决了这个问题。

因此,如果要复制任何以前编译的数据,请小心,删除类文件夹和重新编译后,它起作用了!..

如果有人因为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迁移中。

此错误可能是由未经检查的Java 版本要求引起的。

在我的例子中,我能够在构建一个备受瞩目的开源项目时解决这个错误,方法是使用SDKMAN 从 Java 9 切换到 Java 8! .

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

然后如下所述进行全新安装。


当使用Maven作为你的构建工具时,有时会很有帮助——而且通常是令人满意的,在test disabled的情况下进行干净的“安装”构建。

mvn clean install -DskipTests

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

mvn test

当我没有在项目的 Java 构建路径的“订购和导出”选项卡上导出类时,出现 NoClassDefFound 错误。 确保在添加到项目构建路径的任何依赖项的“订购和导出”选项卡中打勾。 请参阅Eclipse 警告:XXXXXXXXXXX.jar 将不会被导出或发布。 运行时 ClassNotFoundExceptions 可能会导致

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

就我而言,由于 JDK 版本不匹配,我收到此错误。 当我尝试从 Intelij 运行该应用程序时,它无法正常工作,但随后从命令行运行它就可以了。 这是因为 Intelij 试图使用已设置的 Java 11 JDK 运行它,但在命令行上它使用 Java 8 JDK 运行。 在“文件”>“项目结构”>“项目设置”>“项目 SDK”下切换该设置后,它对我有用。

每个人都在这里谈论一些 Java 配置内容、JVM 问题等,在我的情况下,错误与这些主题完全无关,并且有一个非常简单且易于解决的原因:我的 Controller 的端点有一个错误的注释( Spring Boot 应用程序)。

我在使用 Liberty 服务器的 JavaEE 中遇到了一个有趣的问题,即 NoClassDefFoundError。 我正在使用 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包等,但这就是我修复错误的方式,如果其他人已经回答了这个问题,我很抱歉,但我无法将其解释为我的问题。

我对此的解决方案是“利用”缺少的特定类的类路径内容。 在我的例子中,我有 2 个依赖项,虽然我能够使用 javac... 成功编译,但我无法使用 java... 运行生成的类文件,因为 BouncyCastle jar 中的动态类不能在运行时加载。

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 位置即可。 注意我只需要利用一个包含丢失的类文件的罐子。

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

我找到了解决方案。 就我而言,因此定义了静态方法。 JVM 不能创建该类的另一个对象。

例如,

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

Java 在运行时找不到类 A。 A 类位于来自不同工作区的 Maven 项目 ArtClient 中。 所以我将 ArtClient 导入到我的 Eclipse 项目中。 我的两个项目使用 ArtClient 作为依赖项。 我将库引用更改为这些项目的引用(构建路径-> 配置构建路径)。

问题消失了。

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

我的解决方案是:重新启动 Eclipse。 从那以后我再也没有看到这条消息:-)

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

android {
    compileSdkVersion 23
    buildToolsVersion '22.0.1'
    packagingOptions {
    }

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM