NoClassDefFoundError和ClassNotFoundException之间的区别是什么?
是什么导致它们被抛出?如何解决这些问题?
在修改现有代码以包含新的jar文件时,我经常遇到这些可抛出的文件。
我已经在客户端和服务器端为一个通过webstart分发的java应用程序击中了它们。
我发现的可能原因是:
未包含在代码客户端build.xml中的包
我们正在使用的新jar缺少运行时类路径
版本与上一个jar冲突
当我今天遇到这些问题时,我采取了一种反复尝试的方法来让事情正常运行。我需要更多的清晰和理解。
NoClassDefFoundError (NCDFE)发生在代码运行“new Y()”并且无法找到Y类时。
可能只是像其他注释所建议的那样,从类加载器中缺少Y,但也可能是Y类没有签名或具有无效的签名,或者Y是由对代码不可见的其他类加载器加载的,甚至Y依赖于由于上述任何原因而无法加载的Z。
如果发生这种情况,那么JVM将记住加载X的结果(NCDFE),并且每次你请求Y时它都会抛出一个新的NCDFE,而不告诉你为什么:
class a {
static class b {}
public static void main(String args[]) {
System.out.println("First attempt new b():");
try {new b(); } catch(Throwable t) {t.printStackTrace();}
System.out.println("\nSecond attempt new b():");
try {new b(); } catch(Throwable t) {t.printStackTrace();}
}
}
将其保存为a.java文件
代码只是尝试实例化一个新的“b”类两次,除此之外,它没有任何错误,也没有做任何事情。
使用javac . a.java编译代码,然后调用java -cp运行a。A,它应该只打印出两行文本,它应该运行良好,没有错误。
然后删除“a$b.class”文件(或用垃圾填充它,或在上面复制a.class)以模拟缺失或损坏的类。事情是这样的:
First attempt new b():
java.lang.NoClassDefFoundError: a$b
at a.main(a.java:5)
Caused by: java.lang.ClassNotFoundException: a$b
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
... 1 more
Second attempt new b():
java.lang.NoClassDefFoundError: a$b
at a.main(a.java:7)
第一次调用会导致ClassNotFoundException(类装入器在找不到类时抛出),必须将其包装在未检查的NoClassDefFoundError中,因为所讨论的代码(new b())应该能够正常工作。
第二次尝试当然也会失败,但正如您所看到的,包装的异常不再存在,因为ClassLoader似乎记住了失败的类装入器。你只看到了非传染性疾病,完全不知道到底发生了什么。
因此,如果您看到没有根本原因的nccdfe,您需要查看是否可以追溯到类第一次加载时,以查找错误的原因。
通过名称本身,我们可以很容易地识别一个来自异常,另一个来自错误。
Exception:程序执行过程中出现异常。程序员可以通过try catch块处理这些异常。我们有两种类型的异常。在编译时抛出的已检查异常。在运行时抛出的运行时异常,这些异常通常是由于糟糕的编程而发生的。
错误:这些根本不是异常,它超出了程序员的范围。这些错误通常由JVM抛出。
图片来源
的区别:
ClassNotFoundException:
类装入器无法验证链接中的字节代码。
ClassNotFoundException是一个受控异常,当应用程序试图通过类的全限定名加载类,但在类路径上找不到类的定义时,就会发生这种异常。
当使用ClassLoader.loadClass()、class . forname()和ClassLoader.findSystemClass()在运行时提供类名,涉及显式加载类时,就会出现ClassNotFoundException。
NoClassDefFoundError:
类装入器无法解析链接中的类引用。
NoClassDefFoundError是一个从LinkageError类派生的错误,这是一个致命错误。当JVM试图使用new关键字实例化一个类或使用方法调用加载一个类时,无法找到类的定义,就会发生这种情况。
NoClassDefFoundError是由于类的方法调用或任何变量访问而隐式加载类的结果。
相似之处:
NoClassDefFoundError和ClassNotFoundException都与类在运行时不可用有关。
ClassNotFoundException和NoClassDefFoundError都与Java类路径相关。
NoClassDefFoundError (NCDFE)发生在代码运行“new Y()”并且无法找到Y类时。
可能只是像其他注释所建议的那样,从类加载器中缺少Y,但也可能是Y类没有签名或具有无效的签名,或者Y是由对代码不可见的其他类加载器加载的,甚至Y依赖于由于上述任何原因而无法加载的Z。
如果发生这种情况,那么JVM将记住加载X的结果(NCDFE),并且每次你请求Y时它都会抛出一个新的NCDFE,而不告诉你为什么:
class a {
static class b {}
public static void main(String args[]) {
System.out.println("First attempt new b():");
try {new b(); } catch(Throwable t) {t.printStackTrace();}
System.out.println("\nSecond attempt new b():");
try {new b(); } catch(Throwable t) {t.printStackTrace();}
}
}
将其保存为a.java文件
代码只是尝试实例化一个新的“b”类两次,除此之外,它没有任何错误,也没有做任何事情。
使用javac . a.java编译代码,然后调用java -cp运行a。A,它应该只打印出两行文本,它应该运行良好,没有错误。
然后删除“a$b.class”文件(或用垃圾填充它,或在上面复制a.class)以模拟缺失或损坏的类。事情是这样的:
First attempt new b():
java.lang.NoClassDefFoundError: a$b
at a.main(a.java:5)
Caused by: java.lang.ClassNotFoundException: a$b
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
... 1 more
Second attempt new b():
java.lang.NoClassDefFoundError: a$b
at a.main(a.java:7)
第一次调用会导致ClassNotFoundException(类装入器在找不到类时抛出),必须将其包装在未检查的NoClassDefFoundError中,因为所讨论的代码(new b())应该能够正常工作。
第二次尝试当然也会失败,但正如您所看到的,包装的异常不再存在,因为ClassLoader似乎记住了失败的类装入器。你只看到了非传染性疾病,完全不知道到底发生了什么。
因此,如果您看到没有根本原因的nccdfe,您需要查看是否可以追溯到类第一次加载时,以查找错误的原因。
给定类装入器sussystem动作:
这是一篇帮助我理解差异的文章:http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html
If an error occurs during class loading, then an instance of a
subclass of LinkageError must be thrown at a point in the program that
(directly or indirectly) uses the class or interface being loaded.
If the Java Virtual Machine ever attempts to load a class C during
verification (§5.4.1) or resolution (§5.4.3) (but not initialization
(§5.5)), and the class loader that is used to initiate loading of C
throws an instance of ClassNotFoundException, then the Java Virtual
Machine must throw an instance of NoClassDefFoundError whose cause is
the instance of ClassNotFoundException.
所以ClassNotFoundException是NoClassDefFoundError的根本原因。
NoClassDefFoundError是类型加载错误的一种特殊情况,发生在链接步骤。
当试图通过String引用该类来加载该类时,将抛出ClassNotFoundException。例如,Class.forName()中的参数to是一个字符串,这可能会将无效的二进制名称传递给类加载器。
当遇到可能无效的二进制名称时抛出ClassNotFoundException;例如,如果类名有'/'字符,你一定会得到一个ClassNotFoundException。当直接引用的类在类路径上不可用时,它也会被抛出。
另一方面,NoClassDefFoundError被抛出
when the actual physical representation of the class - the .class file is unavailable,
or the class been loaded already in a different classloader (usually a parent classloader would have loaded the class and hence the class cannot be loaded again),
or if an incompatible class definition has been found - the name in the class file does not match the requested name,
or (most importantly) if a dependent class cannot be located and loaded. In this case, the directly referenced class might have been located and loaded, but the dependent class is not available or cannot be loaded. This is a scenario where the directly referenced class can be loaded via a Class.forName or equivalent methods. This indicates a failure in linkage.
简而言之,当类加载器无法找到或加载类定义时,通常会在new()语句或加载先前不存在的类的方法调用上抛出NoClassDefFoundError(与ClassNotFoundException基于字符串的类加载相反)。
最终,当ClassLoader无法加载类时,由ClassLoader实现抛出ClassNotFoundException实例。大多数自定义类加载器实现执行此操作,因为它们扩展了URLClassLoader。通常类加载器不会在任何方法实现上显式抛出NoClassDefFoundError——这个异常通常是由HotSpot编译器中的JVM抛出的,而不是由类加载器本身抛出的。