记一次因static块中代码错误导致出现NoClassDefFoundError异常

在web项目中的某一个请求中使用到了一个工具类来进行某些操作。但是在访问这个请求的时候发现一直报错,提示

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception 
[Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: Could not initialize class com.example.demo.StaticTest] with root cause
java.lang.NoClassDefFoundError: Could not initialize class com.example.demo.StaticTest

其中com.example.demo.StaticTest就是出现问题的类。根据日志,提示StaticTest发生了NoClassDefFoundError异常,也就是这个类不存在。但是我代码中明明有这个类,为啥会提示不存在呢?

原因是我在这个类中使用static块初始化了一些数据,而恰恰在初始化这些数据的时候发生了异常,导致该类并没有被成功的加载到jvm中。但是我隐约记得,如果static发生了异常应该会卡主整个程序才对,为啥其他业务一切正常,只有该请求会报错。于是我编写了如下这么一个类并运行main方法测试。

public class StaticTest {

    private static Integer num;

    static{
        num = 100 / 0;
    }

    public static Integer getNum() {
        return num;
    }

    public static void main(String[] args) {
        System.out.println(StaticTest.getNum());
        System.out.println(StaticTest.getNum());
        System.out.println(StaticTest.getNum());
    }

}

其中我使用100/0模拟发生异常操作。并在main方法中运行了3次getNum()方法。发现在执行第一个getNum方法的时候程序就终止了。

Connected to the target VM, address: '127.0.0.1:54319', transport: 'socket'
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.ArithmeticException: / by zero
	at com.example.demo.StaticTest.<clinit>(StaticTest.java:8)
Disconnected from the target VM, address: '127.0.0.1:54319', transport: 'socket'

Process finished with exit code 1

跟我实际碰到的不太一样,于是我又编写了一个简单的controller,在controller中的请求方法中调用getNum。使用web来访问请求。

在第一次访问请求的时候,会抛出java.lang.ExceptionInInitializerError异常

在后续n次访问请求的时候,则会抛出java.lang.NoClassDefFoundError异常

这次发生的结果就跟我一开始碰到的一样了。但是为啥只有第一次是抛出类初始化异常,而后续全部是抛出类不存在的异常呢?经过查阅资料学习到,类只会被加载一次,加载失败之后再次调用就会抛出类不存在的异常。问题找到了。所以,千万不要在static块中做可能发生异常的操作,如果有也要用trycatch处理掉,不要直接不处理或者抛出异常

以下是查阅到的相关资料:

NoClassDefFoundError和ClassNotFoundException区别

NoClassDefFoundError发生在JVM在动态运行时,根据你提供的类名,在classpath中找到对应的类进行加载,但当它找不到这个类时,就发生了java.lang.NoClassDefFoundError的错误

而ClassNotFoundException是在编译的时候在classpath中找不到对应的类而发生的错误。

ClassNotFoundException比NoClassDefFoundError容易解决,是因为在编译时我们就知道错误发生,并且完全是由于环境的问题导致。

类加载的特性

在JVM的生命周期里,每个类只会被加载一次。

类加载的原则

延迟加载,能少加载就少加载,因为虚拟机的空间是有限的。

类加载的时机

1)第一次创建对象要加载类。

2)调用静态方法时要加载类,访问静态属性时会加载类。

3)加载子类时必定会先加载父类。

4)创建对象引用不加载类。

5)  子类调用父类的静态方法时

    (1)当子类没有覆盖父类的静态方法时,只加载父类,不加载子类

    (2)当子类有覆盖父类的静态方法时,既加载父类,又加载子类

6)访问静态常量,如果编译器可以计算出常量的值,则不会加载类,例如:public static final int a =123;否则会加载类,例如:public static final int a = math.PI。