ClassNotFoundException和NoClassDefFoundError的区别与联系

从命名上来看,一个是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只是其中的一种。

猜你喜欢

转载自blog.csdn.net/qq_37720278/article/details/86676754
今日推荐