我知道,在 Java 中,当一个类不存在时会报错,但是有些时候报的异常是 java.lang.ClassNotFoundException
,有些时候报的错误却是 java.lang.NoClassDefFoundError
。
我有点疑惑,不太清楚java.lang.ClassNotFoundException
和java.lang.NoClassDefFoundError
这两个报错信息之间有什么区别呢?
谁能帮忙解释下吗?
我知道,在 Java 中,当一个类不存在时会报错,但是有些时候报的异常是 java.lang.ClassNotFoundException
,有些时候报的错误却是 java.lang.NoClassDefFoundError
。
我有点疑惑,不太清楚java.lang.ClassNotFoundException
和java.lang.NoClassDefFoundError
这两个报错信息之间有什么区别呢?
谁能帮忙解释下吗?
关于 Class 的加载问题,一般有三个比较常见的异常(或错误):java.lang.ClassNotFoundException
和java.lang.NoClassDefFoundError
,以及 java.lang.ExceptionInInitializerError
。
那么它们是怎么来的?有什么区别呢?
我们先来看看这几个类的层次结构:
从上面的图中,我们可以看出:ClassNotFoundException
继承自 ReflectiveOperationException
(意即反射操作异常),
NoClassDefFoundError
和 ExceptionInInitializerError
继承自 LinkageError
(意即依赖链接错误)。
接着,我们再来看看 Java 官方文档的权威说明(链接是英文的,我简单翻译成中文):
java.lang.ClassNotFoundException
当应用尝试通过类的名称来加载该类时,具体方式如下:
Class.forName()
方法来加载;ClassLoader.findSystemClass()
方法来加载;ClassLoader.loadClass()
方法来加载。如果找不到指定名称的类定义,则抛出该异常。
从JDK 1.4开始,已对该异常进行了改进,以符合通用异常链机制。
如果在加载类时还引起了其他异常,也可以在构造ClassNotFoundException异常时,将其他的异常作为可选参数传入,这也就是我们熟知的"Caused by"异常。构造时传入的可选异常,你可以通过getException() 或Throwable.getCause()
方法获取到,前者是后者的"历史遗留产物(legacy method)"。
java.lang.NoClassDefFoundError
当 Java 虚拟机或一个ClassLoader
类的实例尝试加载一个类的定义(作为常规方法调用或new
表达式的一部分)时,如果找不到该类的定义,则抛出该错误。
这个类在当前正在执行的类被编译的时候还存在,但是在真正运行的时候该类就不存在了,此时就会抛出该错误。
java.lang.ExceptionInInitializerError
表示在静态初始化程序中发生了意外的异常。 抛出ExceptionInInitializerError
表示在执行静态代码初始化或静态变量初始化期间发生了异常。
看完官方文档的描述,我们差不多就能够明白这几个类之前的区别了,下面简单小结一下:
java.lang.ClassNotFoundException
一般是在通过反射方式根据类的名称加载类时触发,如果指定的类不存在,则抛出该异常。java.lang.NoClassDefFoundError
一般是当前代码的执行需要依赖于另外一个类,在编译当前代码的时候,这个类都还好好的,但是在运行时,这个类却找不到了。这种情况就会抛出该错误。java.lang.ExceptionInInitializerError
一般是程序在初始化执行代码的过程中,执行到某些静态代码块或静态变量(比如计算静态变量的值)时发生了异常,则会抛出该错误。如何修复这些异常或错误呢?
一般情况下,当找不到一个类时,就要:
如果你不知道缺失的 Class 文件在哪个jar文件中,你可以通过执行如下 Java 代码来查找:
// 此处以 Apache Commons Lang 3 包中的 StringUtils 类文件举例
// 请自行根据实际情况,修改为对应的类
URL location = StringUtils.class.getProtectionDomain().getCodeSource().getLocation();
System.out.println(location);
// 输出类似于:"file:/D:/Maven/repos/org/apache/commons/commons-lang3/3.9/commons-lang3-3.9.jar"