NoClassDefFoundError 和 ClassNotFoundException 之间的原因和区别是啥?

Posted

技术标签:

【中文标题】NoClassDefFoundError 和 ClassNotFoundException 之间的原因和区别是啥?【英文标题】:What causes and what are the differences between NoClassDefFoundError and ClassNotFoundException?NoClassDefFoundError 和 ClassNotFoundException 之间的原因和区别是什么? 【发布时间】:2010-11-30 06:45:09 【问题描述】:

NoClassDefFoundErrorClassNotFoundException 有什么区别?

是什么导致它们被抛出?如何解决?

在修改现有代码以包含新的 jar 文件时,我经常会遇到这些 throwable。 对于通过 webstart 分发的 java 应用程序,我在客户端和服务器端都点击了它们。

我遇到的可能原因:

    build.xml 中未包含用于客户端代码的包 我们正在使用的新 jar 缺少运行时类路径 版本与之前的 jar 冲突

当我今天遇到这些问题时,我会采取循序渐进的方法来让事情顺利进行。我需要更多的清晰和理解。

【问题讨论】:

我经常发现使用 -verbose(例如 -verbose:class -verbose:jni)运行 JVM 会有所帮助 - 但 mogsie 在他们的回答下方报告说这没有提供额外有用的信息 :( 【参考方案1】:

与 Java API 规范的区别如下。

对于ClassNotFoundException

当应用程序尝试 通过它的字符串加载一个类 名称使用:

Class 类中的 forName 方法。 ClassLoader 类中的 findSystemClass 方法。 ClassLoader 类中的 loadClass 方法。

但没有定义类 可以找到指定的名称。

对于NoClassDefFoundError

如果 Java 虚拟机或 ClassLoader 实例尝试加载 在类的定义中(作为一部分 正常的方法调用或作为 使用 new 创建一个新实例 表达式)并且没有定义 可以找到类。

搜索到的类定义 当前执行时存在 类已编译,但定义 再也找不到了。

因此,似乎NoClassDefFoundError 在源代码编译成功时出现,但在运行时,找不到所需的class 文件。这可能是在 JAR 文件的分发或生产中可能发生的事情,其中​​并未包含所有必需的 class 文件。

至于ClassNotFoundException,它似乎源于试图在运行时对类进行反射调用,但程序试图调用的类并不存在。

两者的区别在于一个是Error,另一个是ExceptionNoClassDefFoundErrorError,它是由 Java 虚拟机在查找它期望找到的类时遇到问题而引起的。由于未找到 class 文件,或者与编译时生成或遇到的文件不同,预期在编译时工作的程序无法运行。这是一个非常严重的错误,因为程序无法由 JVM 启动。

另一方面,ClassNotFoundException 是一个Exception,因此在某种程度上是可以预期的,并且是可以恢复的。使用反射可能容易出错(因为有些期望事情可能不会按预期进行。没有编译时检查以查看所有必需的类是否存在,因此查找所需类的任何问题都会在运行时出现.

【讨论】:

NoClassDefFoundError 通常发生在类的静态块或静态字段初始化出现问题(抛出异常),导致类无法初始化成功。 点赞。一个是Error,另一个是Exception。 :)【参考方案2】:

当 ClassLoader 未找到报告的类时,将引发 ClassNotFoundException。这通常意味着 CLASSPATH 中缺少该类。这也可能意味着有问题的类正试图从另一个类加载,该类加载到父类加载器中,因此子类加载器中的类不可见。在应用服务器等更复杂的环境中工作时有时会出现这种情况(WebSphere 因此类类加载器问题而臭名昭著)。

人们经常将java.lang.NoClassDefFoundErrorjava.lang.ClassNotFoundException 混淆,但有一个重要的区别。例如一个异常(因为java.lang.NoClassDefFoundError 是java.lang.Error 的子类,所以确实是一个错误),比如

java.lang.NoClassDefFoundError:
org/apache/activemq/ActiveMQConnectionFactory

并不意味着 ActiveMQConnectionFactory 类不在 CLASSPATH 中。事实上它恰恰相反。这意味着 ClassLoader 找到了 ActiveMQConnectionFactory 类,但是在尝试加载该类时,它在读取类定义时遇到了错误。这通常发生在所讨论的类具有使用 ClassLoader 未找到的类的静态块或成员时。因此,要找到罪魁祸首,请查看相关类的源代码(在本例中为 ActiveMQConnectionFactory)并查找使用静态块或静态成员的代码。如果您无权访问源代码,则只需使用 JAD 反编译即可。

在检查代码时,假设您找到如下代码行,请确保您的 CLASSPATH 中的 SomeClass 类。

private static SomeClass foo = new SomeClass();

提示:要找出一个类属于哪个 jar,您可以使用网站 jarFinder 。这允许您使用通配符指定类名,并在其 jar 数据库中搜索该类。 jarhoo 允许您做同样的事情,但它不再免费使用。

如果您想在本地路径中找到某个类所属的 jar,您可以使用 jarscan 之类的实用程序 (http://www.inetfeedback.com/jarscan/)。您只需指定要查找的类以及您希望它开始在 jars 和 zip 文件中搜索类的根目录路径。

【讨论】:

有趣的是,这是最后一个完全正确的答案。 (在我投票之前甚至是-1)。 ClassNotFoundException 表示 CL 看不到 .class 文件。 NoClassDefFoundError 表示 .class 文件在那里它不可加载(可能是 JNI 错误)。 这个答案是不是和 coobird 的答案相矛盾? 我尝试了类似的静态块示例。我的班级 Class1 有静态变量“private static B foo = new B();”编译后,我从 bin 文件夹中删除了 B.class 文件。现在,当我创建 Class1 的对象时,来自第三类的 Main 方法。错误如下所示:--------“线程“main”中的异常java.lang.NoClassDefFoundError:spring / B”........所以它准确地提到了它没有找到哪个类ieclass 在静态块中引用,而不是外部类。所以它与这个答案相反。 +1 用于澄清“并不意味着 ActiveMQConnectionFactory 类不在 CLASSPATH 中”【参考方案3】:

NoClassDefFoundError 基本上是一个链接错误。当您尝试实例化一个对象(静态地使用“new”)并且在编译期间找不到它时会发生这种情况。

ClassNotFoundException 更通用,当您尝试使用不存在的类时会出现运行时异常。例如,您在函数中有一个参数接受一个接口,并且有人传入了一个实现该接口的类,但您无权访问该类。它还涵盖了动态类加载的情况,例如使用loadClass()Class.forName()

【讨论】:

【参考方案4】:

当您的代码运行“new Y()”并且找不到 Y 类时,会发生 NoClassDefFoundError (NCDFE)。

可能只是像其他 cmets 建议的那样,您的类加载器中缺少 Y,但也可能是 Y 类未签名或签名无效,或者 Y 由不可见的不同类加载器加载到您的代码,甚至 Y 依赖于由于上述任何原因而无法加载的 Z。

如果发生这种情况,那么 JVM 会记住加载 X (NCDFE) 的结果,并且每次你请求 Y 时它都会简单地抛出一个新的 NCDFE 而不会告诉你原因:

类 静态类 b 公共静态无效主要(字符串参数[]) System.out.println("第一次尝试 new b():"); 试试 新 b(); catch(Throwable t) t.printStackTrace(); System.out.println("\n第二次尝试 new b():"); 试试 新 b(); catch(Throwable t) t.printStackTrace();

在某处保存为 a.java

代码只是尝试实例化一个新的“b”类两次,除此之外,它没有任何错误,也没有做任何事情。

使用javac a.java 编译代码,然后通过调用java -cp . a 运行a -- 它应该只打印出两行文本,并且应该可以正常运行而没有错误。

然后删除“a$b.class”文件(或用垃圾填充它,或将a.class 复制到它上面)以模拟丢失或损坏的类。以下是发生的事情:

第一次尝试新 b(): java.lang.NoClassDefFoundError: a$b 在 a.main(a.java:5) 引起:java.lang.ClassNotFoundException:a$b 在 java.net.URLClassLoader$1.run(URLClassLoader.java:200) 在 java.security.AccessController.doPrivileged(本机方法) 在 java.net.URLClassLoader.findClass(URLClassLoader.java:188) 在 java.lang.ClassLoader.loadClass(ClassLoader.java:307) 在 sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301) 在 java.lang.ClassLoader.loadClass(ClassLoader.java:252) 在 java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320) ... 1 更多 第二次尝试新 b(): java.lang.NoClassDefFoundError: a$b 在 a.main(a.java:7)

第一次调用导致 ClassNotFoundException(由类加载器在找不到类时抛出),它必须包含在未经检查的 NoClassDefFoundError 中,因为有问题的代码 (new b()) 应该可以正常工作。

第二次尝试当然也会失败,但正如您所见,包装的异常不再存在,因为 ClassLoader 似乎记住了失败的类加载器。您只看到 NCDFE,完全不知道到底发生了什么。

因此,如果您看到没有根本原因的 NCDFE,您需要查看是否可以追溯到第一次加载类以找到错误原因。

【讨论】:

如何使用-verbose 运行JVM,或者根据特定的JVM 使用一些类似的选项?如果使用 JNI,可能是 -verbose:class,可能是 -verbose:class:jni,但我不确定语法。如果这有用,也许您可​​以显示结果。 -verbose:class-verbose:jni 都没有提供与缺失类相关的任何额外输出。 感谢您的尝试,即使结果令人失望。 (附言我后来发现-verbose:class:jni 是错误的:必须指定两个单独的选项:-verbose:class -verbose:jni。) 最后一句 * 1,000,000: 因此,如果您看到没有根本原因的 NCDFE,您需要查看是否可以追溯到第一次加载类以找到错误的原因。【参考方案5】:

来自http://www.javaroots.com/2013/02/classnotfoundexception-vs.html

ClassNotFoundException :当类加载器在类路径中找不到所需的类时发生。所以,基本上你应该检查你的类路径并将类添加到类路径中。

NoClassDefFoundError :这个比较难调试和查找原因。当在编译时存在所需的类,但在运行时更改或删除类或类的静态初始化抛出异常时,会抛出此错误。这意味着正在加载的类存在于类路径中,但是该类所需的类之一被删除或编译器无法加载。所以你应该看到依赖于这个类的类。

示例

public class Test1




public class Test 

   public static void main(String[] args)
   
        Test1 = new Test1();    
   


现在编译完两个类后,如果你删除Test1.class文件并运行Test类,它会抛出

Exception in thread "main" java.lang.NoClassDefFoundError: Test
    at Test1.main(Test1.java:5)
Caused by: java.lang.ClassNotFoundException: Test
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    ... 1 more

ClassNotFoundException:当应用程序尝试通过其名称加载类,但找不到具有指定名称的类的定义时抛出。

NoClassDefFoundError:如果 Java 虚拟机尝试加载类的定义并且找不到该类的定义,则抛出此错误。

【讨论】:

使用-verbose 运行JVM 怎么样,或者根据特定的JVM 使用一些类似的选项?可能是-verbose:class,如果使用JNI,可能是-verbose:class:jni,但我不确定语法。 -verbose:class:jni 是错误的,但您可以传递两个单独的选项:-verbose:class -verbose:jni【参考方案6】:

得到每个错误的原因是什么以及如何处理此类错误的任何思考过程?

它们密切相关。当 Java 按名称查找特定类但无法成功加载时,会抛出 ClassNotFoundExceptionNoClassDefFoundError 当 Java 寻找链接到一些现有代码的类时抛出 NoClassDefFoundError,但由于某种原因找不到它(例如,错误的类路径、错误的 Java 版本、错误的库版本)和是彻底致命的,因为它表明某些事情发生了严重错误。

如果你有 C 背景,CNFE 就像 dlopen()/dlsym() 的失败,而 NCDFE 是链接器的问题;在第二种情况下,相关的类文件不应该在您尝试使用它们的配置中实际编译。

【讨论】:

【参考方案7】:

示例 #1:

class A
 void met()
   Class.forName("com.example.Class1");
 

如果com/example/Class1 不存在于任何类路径中,则抛出ClassNotFoundException

示例 #2:

Class B
  void met()
   com.example.Class2 c = new com.example.Class2();
 

如果com/example/Class2在编译B时存在,但在执行时未找到,则抛出NoClassDefFoundError

两者都是运行时异常。

【讨论】:

【参考方案8】:

ClassNotFoundException 在尝试通过字符串引用来加载类时抛出。例如,Class.forName() 中的参数是一个字符串,这增加了将无效二进制名称传递给类加载器的可能性。

当遇到可能无效的二进制名称时抛出 ClassNotFoundException;例如,如果类名有'/' 字符,你一定会得到一个ClassNotFoundException。当直接引用的类在类路径上不可用时也会抛出。

另一方面,NoClassDefFoundError 被抛出

当类的实际物理表示 - .class 文件不可用时, 或该类已在不同的类加载器中加载(通常父类加载器会加载该类,因此无法再次加载该类), 或者如果发现了不兼容的类定义 - 类文件中的名称与请求的名称不匹配, 或(最重要的是)无法定位和加载依赖类。在这种情况下,可能已经找到并加载了直接引用的类,但依赖的类不可用或无法加载。在这种情况下,可以通过 Class.forName 或等效方法加载直接引用的类。这表示链接失败。

简而言之,当类加载器无法找到或加载之前不存在的类(与基于字符串的 ClassNotFoundException 类加载相反)的 new() 语句或方法调用时,通常会抛出 NoClassDefFoundError类定义。

最终,由 ClassLoader 实现在无法加载类时抛出 ClassNotFoundException 的实例。大多数自定义类加载器实现都执行此操作,因为它们扩展了 URLClassLoader。通常类加载器不会在任何方法实现上显式抛出 NoClassDefFoundError - 这个异常通常是从 HotSpot 编译器中的 JVM 抛出的,而不是类加载器本身。

【讨论】:

支持提及“类文件中的名称与请求的名称不匹配”。这是一个很常见的原因。【参考方案9】:

ClassNotFoundException 与 NoClassDefFoundError 的区别

【讨论】:

不是很清楚。 “未在类路径中更新”是模糊/不精确的。这是关于 JAR 不在类路径中存在,或者 JAR 的 错误版本 在类路径中。和拼写错误。而且(叹气)由于您将信息发布为时髦的图形,我们无法解决此问题。【参考方案10】:

通过名称本身,我们可以很容易地识别出一个来自Exception,另一个来自Error

异常:程序执行过程中出现异常。程序员可以通过 try catch 块来处理这些异常。我们有两种例外。在编译时抛出的检查异常。在运行时抛出的运行时异常,这些异常通常是由于编程不当而发生的。

错误:这些根本不是例外,超出了程序员的范围。这些错误通常是由 JVM 抛出的。


image source

区别:

ClassNotFoundException:

类加载器无法验证链接中的字节码。 ClassNotFoundException 是一个检查异常,当应用程序尝试通过其完全限定名称加载一个类并且在类路径中找不到它的定义时发生。 当通过在运行时使用 ClassLoader.loadClass()、Class.forName() 和 ClassLoader.findSystemClass() 提供类的名称来涉及类的显式加载时,ClassNotFoundException 出现。李>

NoClassDefFoundError:

类加载器在链接解析类的引用失败。 NoClassDefFoundError 是从 LinkageError 类派生的错误,这是一个致命错误。当 JVM 在尝试使用 new 关键字或通过方法调用加载类时尝试实例化类时找不到类的定义时会发生这种情况。 NoClassDefFoundError 是类的隐式加载的结果,因为来自该类的方法调用或任何变量访问。

相似之处:

NoClassDefFoundErrorClassNotFoundException 都与运行时类的不可用有关。 ClassNotFoundExceptionNoClassDefFoundError 都与 Java 类路径相关。

【讨论】:

【参考方案11】:

给定类加载器 sussystem 操作:

这篇文章对我理解差异有很大帮助:http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html

如果在类加载过程中发生错误,则 LinkageError 的子类必须在程序中的某个点抛出 (直接或间接)使用正在加载的类或接口。

如果 Java 虚拟机在 验证(§5.4.1)或解决(§5.4.3)(但不是初始化 (§5.5)),以及用于启动 C 加载的类加载器 抛出 ClassNotFoundException 的实例,然后是 Java Virtual 机器必须抛出 NoClassDefFoundError 的实例,其原因是 ClassNotFoundException 的实例。

所以 ClassNotFoundExceptionNoClassDefFoundError 的根本原因。NoClassDefFoundError 是类型加载错误的一种特殊情况,发生在 Linking 步骤。

【讨论】:

【参考方案12】:

在实践中添加一个可能的原因:

ClassNotFoundException:正如 cletus 所说,您使用接口,而接口的继承类不在类路径中。例如,服务提供者模式(或Service Locator)尝试定位一些不存在的类 NoClassDefFoundError: 找到给定类,但找不到给定类的依赖项

在实践中,错误可能会静默抛出,例如,您提交一个计时器任务并在它会抛出 Error 的计时器任务,而在大多数情况下,您的程序只会捕获 Exception。然后 Timer 主循环在没有任何信息的情况下结束。与 NoClassDefFoundError 类似的错误是 ExceptionInInitializerError,当您的静态初始化程序或静态变量的初始化程序抛出异常时。

【讨论】:

【参考方案13】:

ClassNotFoundException 是当我们告诉 JVM 使用 Class.forName() 或 ClassLoader.findSystemClass() 或 ClassLoader.loadClass() 方法通过其字符串名称加载类时发生的检查异常,并提到在类路径中找不到类。

大多数情况下,当您尝试运行应用程序而不使用所需的 JAR 文件更新类路径时会发生此异常。例如,您可能在执行 JDBC 代码以连接到数据库(即 mysql)时看到此异常,但您的类路径没有 JAR。

NoClassDefFoundError 错误发生在 JVM 尝试加载作为代码执行一部分的特定类(作为正常方法调用的一部分或作为使用 new 关键字创建实例的一部分)和该类不存在于您的类路径中,但在编译时存在,因为为了执行您的程序,您需要编译它,如果您尝试使用不存在的类,编译器会引发编译错误。

以下是简要说明

您可以阅读Everything About ClassNotFoundException Vs NoClassDefFoundError了解更多详情。

【讨论】:

【参考方案14】:

当我需要刷新时,我会一次又一次地提醒自己以下内容

ClassNotFoundException

类层次结构

ClassNotFoundException extends ReflectiveOperationException extends Exception extends Throwable

调试时

    必需的 jar,类路径中缺少类。 验证所有需要的 jar 都在 jvm 的类路径中。

NoClassDefFoundError

类层次结构

NoClassDefFoundError extends LinkageError  extends Error extends Throwable

调试时

    动态加载类的问题,该类已正确编译 依赖类的静态块,构造函数,init()方法的问题,实际错误被多层包裹[尤其是当你使用spring时,休眠实际异常被包裹,你会得到NoClassDefError] 当您在依赖类的静态块下遇到“ClassNotFoundException”时 类版本问题。 当您在不同的 jar/包下有相同类的两个版本 v1、v2 时会发生这种情况,这些版本使用 v1 成功编译,并且 v2 在没有相关方法/变量的运行时加载,您将看到此异常。 [我曾经通过删除类路径中出现的多个 jar 下的 log4j 相关类的副本来解决此问题]

【讨论】:

【参考方案15】:

ClassNotFoundException 和 NoClassDefFoundError 在运行时找不到特定类时发生。但是,它们发生在不同的场景中。

ClassNotFoundException 是当您尝试在运行时使用 Class.forName() 或 loadClass() 方法加载类并且在类路径中找不到所提及的类时发生的异常。

    public class MainClass
    
        public static void main(String[] args)
        
            try
            
                Class.forName("oracle.jdbc.driver.OracleDriver");
            catch (ClassNotFoundException e)
            
                e.printStackTrace();
            
        
    



    java.lang.ClassNotFoundException: oracle.jdbc.driver.OracleDriver
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Unknown Source)
    at pack1.MainClass.main(MainClass.java:17)

NoClassDefFoundError 是在编译时存在特定类但在运行时丢失时发生的错误。

    class A
    
      // some code
    
    public class B
    
        public static void main(String[] args)
        
            A a = new A();
        
    

编译上述程序时,会生成两个.class文件。一个是A.class,另一个是B.class。如果删除 A.class 文件并运行 B.class 文件,Java Runtime System 将抛出 NoClassDefFoundError 如下:

    Exception in thread "main" java.lang.NoClassDefFoundError: A
    at MainClass.main(MainClass.java:10)
    Caused by: java.lang.ClassNotFoundException: A
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)

【讨论】:

以上是关于NoClassDefFoundError 和 ClassNotFoundException 之间的原因和区别是啥?的主要内容,如果未能解决你的问题,请参考以下文章

使用Java API 操作Hbase表出现异常:Exception in thread "main" java.lang.NoClassDefFoundError

Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/logging/LogF

NoClassDefFoundError - Eclipse 和 Android

NoClassDefFoundError 和 ClassNotFoundException 之间的原因和区别是啥?

NoClassDefFoundError和ClassNotFoundException区别

ClassNotFoundException和NoClassDefFoundError