两种错误都是涉及类加载问题,类层次结构如下:
NoClassDefFoundError是系统错误,ClassNotFoundException是系统异常,可以捕获。 NoClassDefFoundError发生在对Class原始文件解析通过类的全限定名在类路径下无法找到相关类的定义时;ClassNotFoundException发生在通过调用Class.forName()、类加载器的loadClass/findClass在程序中自定义加载类时。
- 注: 以下测试代码文件夹层次结构: *
下面为NoClassDefFoundError测试程序,TestReference.java程序内引用google.guava包里的第三方类。在编译时将第三方的guava-23.0.jar加入到classpath里,编译通过,运行时不加classpath,然后程序就会抛出NoClassDefFoundError,通过异常堆栈可以看出,底层也是通过loadClass来尝试加载引用的类的。
import com.google.common.base.*;
import com.google.common.collect.*;
import java.util.*;
public class TestReference{
public static void main(String[] args){
Map<String,String> a = Maps.newHashMap();
}
}
javac -cp guava-23.0.jar TestReference.java
// 编译OK
java TestReference
// 报错:
// Exception in thread "main" java.lang.NoClassDefFoundError: com/google/common/col
// lect/Maps
// at TestReference.main(TestReference.java:6)
// Caused by: java.lang.ClassNotFoundException: com.google.common.collect.Maps
// at java.net.URLClassLoader.findClass(Unknown Source)
// at java.lang.ClassLoader.loadClass(Unknown Source)
// at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
// at java.lang.ClassLoader.loadClass(Unknown Source)
// ... 1 more
java TestReference -cp .;guava-23.0.jar
// 执行OK
通过研究Class文件结构可以得出以下结论: 当Java程序运行时,JVM加载TestReference.class文件,解析出methodRef下code属性里的代码内容,通过代码里写的类短名称加上import的空间定位引用类的全限定名,然后通过全限定名在类路径下加载相关的类,当没有找到时抛出该错误。这个错误是JVM抛出。
下面是ClassNotFoundException示例代码,ClassNotFoundException代码里通过Class.forName("") 加载google.guava的类:
public class TestClassForname{
public static void main(String[] args) throws Exception{
Class a = Class.forName("com.google.common.collect.Maps");
}
}
javac TestClassForname.java
// 编译OK
java TestClassForname
// Exception in thread "main" java.lang.ClassNotFoundException: com.google.common.c
// ollect.Maps
// at java.net.URLClassLoader.findClass(Unknown Source)
// at java.lang.ClassLoader.loadClass(Unknown Source)
// at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
// at java.lang.ClassLoader.loadClass(Unknown Source)
// at java.lang.Class.forName0(Native Method)
// at java.lang.Class.forName(Unknown Source)
// at TestClassForname.main(TestClassForname.java:4)
ClassNotFoundException这个异常的定义就是没有加载到类就抛出这个异常,上面说的NoClassDefFoundError底层也是通过抛出这个异常触发的,只不过JVM会在上面场合下通过捕获ClassNotFoundException然后转而抛出NoClassDefFoundError错误。