最近看到一个面试题,问ClassNotFoundException和NoClassDefFoundError的区别。平时没有专门思考过这个问题,藉此机会分析一下。
一个直观的区别是一个是异常,一个是错误,异常和错误的区别就可以先讲一波。
然后其他的相同点和不同点在看到的一篇非常好的英文文章中有详细的讲述,翻译在此。
ClassNotFoundException 和NoClassDefFoundError都发生在JVM在classpath下找不到所需的类时。
虽然看起来很相似,但是两者有很大不同。
本文我们将介绍他们是怎样出现的以及怎么去解决这些错误。
当应用尝试在类路径中用全限量名去加载某个类时,如果找你不到它的定义就会报CLassNotFoundException 。它是一个可检测异常。
通常出现在用Class.forName(), ClassLoader.loadClass()或 ClassLoader.findSystemClass()这三个方法加载类的时候。我们在使用反射的时候,要特别注意这个异常。
下面这个例子,我们尝试加载的JDBC驱动没有添加到类路径中(没有添加依赖),将会报ClassNotFoundException。
@Test(expected = ClassNotFoundException.class)
public void givenNoDrivers_whenLoadDriverClass_thenClassNotFoundException()
throws ClassNotFoundException {
Class.forName("oracle.jdbc.driver.OracleDriver");
}
NoClassDefFoundError是一种致命错误。
当JVM尝试通过new关键字创建一个类实例或者方法调用来加载一个类时找不到这个类的定义就会出现这个错误。
通常是编译时正常编译,但是运行时找不到这个类。
通常发生在执行动态代码块或者初始化静态字段时报了异常,从而导致类初始化失败而引发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();
return test;
}
}
编写测试用例
@Test(expected = NoClassDefFoundError.class)
public void givenInitErrorInClass_whenloadClass_thenNoClassDefFoundError() {
NoClassDefFoundErrorExample sample
= new NoClassDefFoundErrorExample();
sample.getClassWithInitErrors();
}
排查和修复这两个问题有时候会非常耗时。
他们的主要原因是运行时类路径中类文件不可用。
下面是几点具体的原因:
ClassNotFoundException与NoClassDefException核心区别是,前者强调运行时无法匹配到指定参数名称的类,后者强调编译时没问题,运行时却无法实例化一个类。
最常见的解决方法是检查是否依赖了相关包或者相关包是否有冲突。
英文原文:https://www.baeldung.com/java-classnotfoundexception-and-noclassdeffounderror
源代码地址:https://github.com/eugenp/tutorials/tree/master/core-java-lang