java.lang.NoClassDefFoundError: Could not initialize class xxx

599 阅读2分钟

结论

(1)NoClassDefFoundError 是个 Error,是指一个 class 在编译时存在,在运行时找不到了 class 文件了。

(2)一个类没有初始化成功,其他类调用这个类,导致执行失败。

复现问题:

有两种场景会导致这个问题出现

第一个场景

class B {
     public static void main(String[] args) {
         A a = new A();
     }
}

public class A {
    public void hello(){
        System.out.println("hello world!");
    }
}

上面的Java类编译后会生成两个类文件,一个A.class,一个B.class,现在我在编译后,删掉了A的class文件,然后直接执行B的main方法,就会抛出NoClassDefFoundError错误。

原因:

因为当执行到 A a=new A(); 这一步的时候,JVM认为这个类肯定在当前的classpath里面的,要不然编译都不会通过。

既然它存在,那么在JVM里面一定能找到,如果不能找到,那就说明出大事了,因为编译和运行不一致,所以直接抛出这个ERROR,代表问题很严重。

第二个场景

一个类没有初始化成功,其他类调用这个类,导致执行失败。

故意写一个初始化失败的类

public class InitFail {
    static double divideZero = 1/0;//故意使得类初始化失败.
    public static void print(){
        System.out.println("此行不会打印");
    }
}

构建消费类

public static void main(String[] args) {
    try {
        double divideZero = InitFail.divideZero;
    }catch (Throwable e){
    // 此处,必须用Throwable,用Exception会直接退出.
        System.out.println(e);
    }
    // 继续使用.
    InitFail.print();
}

原因

这种情况比较特殊,并不是因为编译时和运行时环境不一致导致的,而是对于一个类如果初始化失败后,还继续使用。

JVM会认为是不正常的,由于它第一次调用已经失败,JVM就会假设后面继续调用肯定仍然会失败,所以直接抛ERROR给客户端。

彩蛋

JVM在加载类的时候,会初始化类里的静态变量,或执行静态块,如果这个时候抛出了异常,该类就会加载失败,那么以后任何使用到这个类的地方,都会抛出NoClassDefFoundError异常。

image.png