NoClassDefFoundError
和 ClassNotFoundException
之间有什么区别?
是什么导致他们被抛出?他们怎么解决?
在修改现有代码以包含新的jar文件时,我经常遇到这些throwable . 对于通过webstart分发的Java应用程序,我在客户端和服务器端都点击了它们 .
我遇到的可能原因:
-
包未包含在
build.xml
中,用于代码的客户端
我们正在使用的新 jar 缺少 -
运行时类路径
-
版本与之前的jar冲突
当我今天遇到这些时,我采取了一种线索和错误的方法来使事情发挥作用 . 我需要更清晰和理解 .
15 回答
示例#1:
如果
com/example/Class1
在任何类路径中都不存在,那么它会抛出ClassNotFoundException
.示例#2:
如果编译B时存在
com/example/Class2
,但执行时未找到,则抛出NoClassDefFoundError
.两者都是运行时异常 .
与Java API规范的不同之处如下 .
对于ClassNotFoundException:
对于NoClassDefFoundError:
因此,似乎在成功编译源时发生
NoClassDefFoundError
,但在运行时,找不到所需的class
文件 . 这可能是在分发或生成JAR文件时可能发生的事情,其中并未包含所有必需的class
文件 .至于
ClassNotFoundException
,它似乎可能源于尝试在运行时对类进行反射调用,但程序试图调用的类不存在 .两者之间的区别在于,一个是
Error
,另一个是Exception
.NoClassDefFoundError
是一个Error
,它来自Java虚拟机,在查找预期找到的类时遇到问题 . 由于未找到class
文件,或者与编译时生成或遇到的文件不同,因此预计在编译时工作的程序无法运行 . 这是一个非常严重的错误,因为程序无法由JVM启动 .另一方面,
ClassNotFoundException
是Exception
,所以它有点预期,并且是可以恢复的 . 使用反射可能容易出错(因为有些期望事情可能没有按预期进行 . 没有编译时检查以查看所有必需的类都存在,因此找到所需类的任何问题都将在运行时出现 .当ClassLoader找不到报告的类时,抛出ClassNotFoundException . 这通常意味着CLASSPATH中缺少该类 . 它也可能意味着有问题的类试图从另一个加载在父类加载器中的类加载,因此子类加载器中的类是不可见的 . 在像App Server这样的更复杂的环境中工作时有时就是这种情况(WebSphere对于这样的类加载器问题是臭名昭着的) .
人们常常倾向于将
java.lang.NoClassDefFoundError
与java.lang.ClassNotFoundException
混淆,但是有一个重要的区别 . 例如一个异常(一个错误,因为java.lang.NoClassDefFoundError
是java.lang.Error的子类)就像并不意味着ActiveMQConnectionFactory类不在CLASSPATH中 . 事实上,恰恰相反 . 这意味着ClassLoader找到了类ActiveMQConnectionFactory,但是在尝试加载类时,它会在读取类定义时遇到错误 . 当有问题的类具有静态块或使用ClassLoader未找到的类的成员时,通常会发生这种情况 . 因此,要查找罪魁祸首,请查看相关类的源(本例中为ActiveMQConnectionFactory),并使用静态块或静态成员查找代码 . 如果您没有访问源代码,那么只需使用JAD对其进行反编译即可 .
在检查代码时,假设您找到了如下所示的代码行,请确保CLASSPATH中的类SomeClass .
提示:要找出类所属的jar,可以使用网站jarFinder . 这允许您指定一个类使用通配符命名,并在其jar数据库中搜索该类 . jarhoo允许你做同样的事情,但它不再免费使用 .
如果要在本地路径中找到类属于哪个jar,可以使用jarscan(http://www.inetfeedback.com/jarscan/)之类的实用程序 . 你只需指定你喜欢的类就可以开始在jar和zip文件中搜索类了 .
NoClassDefFoundError基本上是一个链接错误 . 当您尝试实例化一个对象(静态地使用“new”)并且在编译期间找不到它时,就会发生这种情况 .
ClassNotFoundException更通用,当您尝试使用不存在的类时,它是一个运行时异常 . 例如,函数中的参数接受接口,有人传入实现该接口的类但您无权访问该类 . 它还包括动态类加载的情况,例如使用loadClass()或Class.forName() .
当您的代码运行“new Y()”并且找不到Y类时,会发生NoClassDefFoundError(NCDFE) .
可能只是你的类加载器中缺少Y,就像其他注释所暗示的那样,但可能是Y类没有签名或者签名无效,或者Y是由代码看不到的其他类加载器加载的,甚至Y依赖于Z,由于上述任何原因无法加载 .
如果发生这种情况,那么JVM将记住加载X(NCDFE)的结果,并且每次请求Y时它只会抛出一个新的NCDFE而不告诉您原因:
将其保存为某处的a.java
代码只是尝试两次实例化一个新的“b”类,除此之外,它没有任何错误,并且它没有做任何事情 .
使用
javac a.java
编译代码,然后通过调用java -cp . a
运行 - 它应该只打印出两行文本,它应该运行正常而没有错误 .然后删除“a $ b.class”文件(或用垃圾填充它,或在其上复制a.class)来模拟丢失或损坏的类 . 这是发生的事情:
第一次调用导致ClassNotFoundException(由类加载器在找不到类时抛出),它必须包含在未经检查的NoClassDefFoundError中,因为有问题的代码(
new b()
)应该可以正常工作 .第二次尝试当然也会失败,但是你可以看到包装的异常不再存在,因为ClassLoader似乎记得失败的类加载器 . 你只看到NCDFE完全不知道究竟发生了什么 .
因此,如果您看到没有根本原因的NCDFE,您需要查看是否可以追溯到第一次加载类以查找错误原因 .
来自http://www.javaroots.com/2013/02/classnotfoundexception-vs.html:
ClassNotFoundException :当类加载器在类路径中找不到所需的类时发生 . 所以,基本上你应该检查你的类路径并在类路径中添加类 .
NoClassDefFoundError :这更难以调试并找到原因 . 当在编译时存在所需的类时抛出此异常,但在运行时更改或删除类或类的静态初始化引发异常 . 这意味着正在加载的类存在于类路径中,但此类所需的类之一被删除或无法由编译器加载 . 所以你应该看到依赖于这个类的类 .
Example :
现在编译完两个类后,如果删除Test1.class文件并运行Test类,它将抛出
ClassNotFoundException
:当应用程序尝试通过其名称加载类时抛出,但是找不到具有指定名称的类的定义 .NoClassDefFoundError
:如果Java虚拟机尝试加载类的定义并且找不到类的定义,则抛出 .他们密切相关 . 当Java按名称查找特定类并且无法成功加载它时,抛出
ClassNotFoundException
. 当Java寻找链接到某些现有代码的类时,会抛出NoClassDefFoundError
,但由于某种原因无法找到它(例如,错误的类路径,错误的Java版本,库的错误版本)并且是彻底的这是致命的,因为它表明事情已经发生了严重错误 .如果你有一个C背景,CNFE就像是
dlopen()
/dlsym()
的失败,NCDFE是链接器的问题;在第二种情况下,类文件应该永远不会在您尝试使用它们的配置中实际编译 .当尝试通过String引用类来加载类时,抛出ClassNotFoundException . 例如,Class.forName()中的参数是一个String,这会增加传递给类加载器的无效二进制名称的可能性 .
遇到可能无效的二进制名称时抛出ClassNotFoundException;例如,如果类名具有'/'字符,则必然会得到ClassNotFoundException . 当类路径上没有直接引用的类时,它也会抛出 .
另一方面,抛出NoClassDefFoundError
当类的实际物理表示 - .class文件不可用时,
或者类已经加载到不同的类加载器中(通常父类加载器会加载类,因此无法再次加载类),
或者如果找到了不兼容的类定义 - 类文件中的名称与请求的名称不匹配,
或(最重要的)如果无法定位和加载依赖类 . 在这种情况下,可能已找到并加载了直接引用的类,但是依赖类不可用或无法加载 . 这是一种可以通过Class.forName或等效方法加载直接引用的类的方案 . 这表明链接失败 .
简而言之,当类加载器无法找到或加载类定义时,通常会在new()语句或方法调用上抛出NoClassDefFoundError,这些调用会加载以前缺少的类(而不是基于字符串的ClassNotFoundException类加载)( S) .
最终,ClassLoader实现在无法加载类时抛出ClassNotFoundException实例 . 大多数自定义类加载器实现都会执行此操作,因为它们扩展了URLClassLoader . 通常类加载器不会在任何方法实现上显式抛出NoClassDefFoundError - 这个异常通常是从HotSpot编译器中的JVM抛出的,而不是由类加载器本身引发的 .
ClassNotFoundException与NoClassDefFoundError之间的区别
Exception: 程序执行期间发生异常 . 程序员可以通过try catch块处理这些异常 . 我们有两种类型的例外 . 检查在编译时抛出的异常 . 在运行时抛出的运行时异常,这些异常通常是由于编程错误而发生的 .
Error: 这些都不是例外,它超出了程序员的范围 . 这些错误通常由JVM抛出 .
image source
Difference:
ClassNotFoundException:
类加载器无法 verify 我们在 Link phase 中提到的类字节代码 class loading subsystem 我们得到
ClassNotFoundException
.ClassNotFoundException
是直接从java.lang.Exception
类派生的已检查异常,您需要为其提供显式处理当通过使用ClassLoader.loadClass(),Class.forName()和ClassLoader.findSystemClass()在运行时提供类的名称而涉及到 explicit loading 类时,会出现
ClassNotFoundException
.NoClassDefFoundError:
类加载器无法 resolving Link phase 中的一个类的引用 class loading subsystem 我们得到
NoClassDefFoundError
.NoClassDefFoundError
是一个派生自LinkageError
类的错误,它用于指示错误情况,其中一个类依赖于某个其他类,并且该类在编译后不兼容地更改 .NoClassDefFoundError
是 implicit loading 类的结果,因为来自该类或任何变量访问的方法调用 .Similarities:
NoClassDefFoundError
和ClassNotFoundException
都与运行时类的不可用性有关 .ClassNotFoundException
和NoClassDefFoundError
都与Java类路径相关 .给定类加载器sussystem操作:
这篇文章帮助我了解了很多不同之处:http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html
所以ClassNotFoundException是NoClassDefFoundError的根本原因 .
NoClassDefFoundError是类型加载错误的特殊情况,发生在 Linking 步骤 .
在实践中添加一个可能的原因:
ClassNotFoundException:正如cletus所说,你使用接口而继承类接口不在classpath中 . 例如,服务提供商模式(或Service Locator)尝试找到一些不存在的类
NoClassDefFoundError:找不到给定的类,但找不到给定类的依赖关系
在实践中,可能会抛出 Error ,例如,您提交了一个计时器任务,并且在计时器任务中它会抛出 Error ,而在大多数情况下,您的程序只会捕获 Exception . 然后 Timer 主循环结束,没有任何信息 . 当静态初始化程序或静态变量的初始化程序抛出异常时,与NoClassDefFoundError类似的错误是ExceptionInInitializerError .
当我需要刷新时,我一次又一次地提醒自己
ClassNotFoundException
Class Hierarchy
While debugging
类路径中缺少必需的jar类 .
验证所有必需的jar都在jvm的classpath中 .
NoClassDefFoundError
Class Hierarchy
While debugging
动态加载类的问题,编译正确
依赖类的静态块,构造函数,init()方法的问题和实际错误由多个层包装[特别是当你使用spring时,hibernate实际的异常被包装,你将得到NoClassDefError]
当您在依赖类的静态块下面临"ClassNotFoundException"时
类的版本有问题 . 当你有两个版本v1,v2在不同jar /包下的同一个类时,会发生这种情况,使用v1成功编译,v2在没有相关方法/ vars的运行时加载,你会看到这个异常 . [我曾经通过删除类路径中出现的多个jar下的log4j相关类的副本解决了这个问题]
ClassNotFoundException 是一个经过检查的异常,当我们告诉JVM使用Class.forName()或ClassLoader.findSystemClass()或ClassLoader.loadClass()方法通过其字符串名称加载类时,并且在类路径中找不到提到的类 .
大多数情况下,当您尝试运行应用程序而不使用所需的JAR文件更新类路径时,会发生此异常 . 例如,在执行JDBC代码连接到数据库(即MySQL)但您的类路径没有JAR时,您可能已经看到过此异常 .
当JVM尝试加载作为代码执行一部分的特定类时(作为正常方法调用的一部分或作为使用new关键字创建实例的一部分)并且该类不存在于类路径中但发生了错误,则会发生 NoClassDefFoundError 错误在编译时出现,因为为了执行你的程序你需要编译它,如果你正在尝试使用一个不存在的类,编译器将引发编译错误 .
以下是简要说明
您可以阅读Everything About ClassNotFoundException Vs NoClassDefFoundError了解更多详情 .
在运行时未找到特定类时会发生ClassNotFoundException和NoClassDefFoundError . 但是,它们出现在不同的方案中 .
ClassNotFoundException是在尝试使用Class.forName()或loadClass()方法在运行时加载类时发生的异常,并且在类路径中找不到提到的类 .
NoClassDefFoundError是在编译时出现特定类但在运行时丢失时发生的错误 .
编译上述程序时,将生成两个.class文件 . 一个是A.class,另一个是B.class . 如果删除A.class文件并运行B.class文件,Java Runtime System将抛出NoClassDefFoundError,如下所示: