一个直观的区别是一个是异常,一个是错误。
ClassNotFoundException
当应用尝试在类路径中用全限量名去加载某个类时,如果找你不到它的定义就会报CLassNotFoundException 。它是一个可检测异常。
通常出现在用Class.forName(), ClassLoader.loadClass()或 ClassLoader.findSystemClass()这三个方法加载类的时候。我们在使用反射的时候,要特别注意这个异常。
NoClassDefFoundError
NoClassDefFoundError是一种致命错误。
当JVM尝试通过new关键字创建一个类实例或者方法调用来加载一个类时找不到这个类的定义就会出现这个错误。
(NoClassDefFoundError 大概率是 ExceptionInInitializerError导致的)
通常是编译时正常编译,但是运行时找不到这个类。
通常发生在执行动态代码块或者初始化静态字段时报了异常,从而导致类初始化失败而引发NoClassDefFoundError。
public class ClassWithInitErrors {
static int data = 1 / 0;
}
public class NoClassDefFoundErrorExample {
public ClassWithInitErrors getClassWithInitErrors() {
ClassWithInitErrors test;
try {
test = new ClassWithInitErrors();
} catch (Throwable t) {
System.out.println(t);
}
test = new ClassWithInitErrors();//在调用一个静态字段初始化有问题的class的时候,第一次报错是ExceptionInInitializerError,第二次再调用就报错NoClassDefFoundError
return test;
}
}
解决办法
排查和修复这两个问题有时候会非常耗时。
他们的主要原因是运行时类路径中类文件不可用。
下面是几点具体的原因:
- 排查所需的jar包是否在类路径中,如果没有就添加进去。
- 如果发现类在classpath里面,很有可能是classpath被重写了,需要再次确定应用准确的classpath
- 依赖包发生了冲突,比如应该依赖高版本jar包,但又其它包传递依赖了低版本jar包,导致高版本中某些类找不到。
- 如果应用中用到了多个类加载器,一个类加载器加载的类,无法再其他的类加载器中使用。
总结
ClassNotFoundException与NoClassDefException核心区别是,前者强调运行时无法匹配到指定参数名称的类,后者强调编译时没问题,运行时却无法实例化一个类。
最常见的解决方法是检查是否依赖了相关包或者相关包是否有冲突。