从命名上来看,一个是exception而另一个是error,这意味着出现ClassNotFoundException时是可以进行异常处理来挽救程序的,但出现NoClassDefFoundError对jvm来说是一个致命的,不可恢复的错误,通常会导致程序crash。
NoClassDefFoundError在jdk中的定义:
/**
* Thrown if the Java Virtual Machine or a <code>ClassLoader</code> instance
* tries to load in the definition of a class (as part of a normal method call
* or as part of creating a new instance using the <code>new</code> expression)
* and no definition of the class could be found.
* <p>
* The searched-for class definition existed when the currently
* executing class was compiled, but the definition can no longer be
* found.
*/
class NoClassDefFoundError extends LinkageError {
当jvm尝试加载一个理应存在的类定义却找不到时,会抛出NoClassDefFoundError。为什么说理应存在呢?假如类A中有段代码引用了类B,那么经过编译后,类A的常量池中会有类B的符号引用,在执行到该段代码时,jvm会让类A的定义类加载器拿着这个符号引用去相应的classpath路径下找类B的定义。jvm肯定是相信编译器的,既然编译器都告诉了jvm类B的坐标(符号引用),那么jvm就会理所应当的认为拿着这个坐标一定能找到类B的定义,若找不到,jvm只能认为此刻发生了严重的错误,应该终止程序的执行。
与NoClassDefFoundError类似的错误还有NoSuchMethodError,它们均都意味着jvm拿着”编译器给的“符合引用去找相关的定义却没找到,这两种错误多见于web项目的迭代开发中。
ClassNotFoundException在jdk中的定义
/**
* Thrown when an application tries to load in a class through its
* string name using:
* <ul>
* <li>The <code>forName</code> method in class <code>Class</code>.
* <li>The <code>findSystemClass</code> method in class
* <code>ClassLoader</code> .
* <li>The <code>loadClass</code> method in class <code>ClassLoader</code>.
* </ul>
* <p>
* but no definition for the class with the specified name could be found.
*/
public class ClassNotFoundException extends ReflectiveOperationException {
当给定类的全限定名让类加载器去加载一个类时,若找不到类的定义,则抛出该异常。这里类的全限定名通常由一个string存放或以字符串常量的身份存在于类的常量池中,假如以Classloader.loadClass(name)的方式运行,jvm在执行到该段代码时只知道name是一串简单字符串,然后会拿着它去classpath路径下尝试寻找对应的类定义,若找不到则抛出ClassNotFoundException,让程序员自己去处理。
可以这样理解:ClassNotFoundException是一个更一般的异常,不论jvm通过类的符合引用还是通过类的全限定名,只有没找到相应的类定义,都会出现ClassNotFoundException;而NoClassDefFoundError是jvm的内部错误。下面通过例子说明两者间的联系。
package test;
public class A {
public static void main(String[] args) {
try {
B b = new B();
}catch (Throwable e){
e.printStackTrace();
}
B b = new B();
}
}
package test;
public class B {
static int i = 1/0;
}
/*输出结果:
java.lang.ExceptionInInitializerError
at test.A.main(A.java:6)
Caused by: java.lang.ArithmeticException: / by zero
at test.B.<clinit>(B.java:4)
... 1 more
Exception in thread "main" java.lang.NoClassDefFoundError: Could not initialize class test.B
at test.A.main(A.java:10)
*/
类A中新建类B的对象,第一次抛出的是ExceptionInInitializerError,表示初始化类B失败了,原因是发生了除零运算。jvm会保存解析每个符号引用的结果,如果第一次解析失败,则后续不再尝试解析,返回结果始终与第一次的结果保持一致。因此第二次直接抛出了NoClassDefFoundError,原因是无法初始化类B。
如果在运行时将类B的class文件从classpath中删掉,程序输出如下:
java.lang.NoClassDefFoundError: test/B
at test.A.main(A.java:6)
Caused by: java.lang.ClassNotFoundException: test.B
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 1 more
Exception in thread "main" java.lang.NoClassDefFoundError: test/B
at test.A.main(A.java:10)
第一次直接抛出NoClassDefFoundError,原因是发生了ClassNotFoundException,即找不到类B的定义。
这里可以将NoClassDefFoundError看作是一个更高层的异常,而造成该异常的原因有很多,ClassNotFoundException只是其中的一种。