NoClassDefFoundError 和 ClassNotFoundException的区别

Java程序员经常会碰见NoClassDefFoundError和ClassNotFoundException,这两个错误都是什么呢?

Java的Throwable定义了所有可以被throw和catch的类的超类,有两个子类,Error和Exception。为了编译期间检查异常的目的,将Throwable及它的子类,但又不是RuntimeException及其子类或Error及其子类的异常称为checked Exceptions。意思是,除了上述两种异常, 其他的异常需要在编译期间检查,如果可能发生,就必须有try语句和字句,或者对应的抛出处理,否则编译不能通过。原文描述如下。

The Throwable class is the superclass of all errors and exceptions in the Java language. Only objects that are instances of this class (or one of its subclasses) are thrown by the Java Virtual Machine or can be thrown by the Java throw statement. Similarly, only this class or one of its subclasses can be the argument type in a catchclause. For the purposes of compile-time checking of exceptions, Throwable and any subclass of Throwable that is not also a subclass of either RuntimeException or Error are regarded as checked exceptions.

可以看到,Error和Exception有共同的父类,但Error和RuntimeException一样,编译期不检查,运行时会抛出。其他异常编译期会检查,要求程序员处理这些异常。

也有人说Exception表示可恢复的异常,Error表示不可恢复的异常,可是,只要是Throwable或者它的子类,都是可以被捕获的,捕获以后,程序把Throwable处理了,都是可以继续运行的。所以这种说法是文档性的约束,java的语法并没有这样的要求。但Java文档说明Error是非常严重的错误,不建议程序自己捕获Error。当然不建议不代表不能。原文描述如下:

An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions. The ThreadDeath error, though a "normal" condition, is also a subclass of Error because most applications should not try to catch it.

所以从描述起码可以看出来,ClassNotFoundException是不严重的异常,可以捕获,而NoClassDefFoundError是非常严重的异常,不建议捕获。


那么这两个错误有什么区别呢?我们查阅 JVM的规范文档, 官方链接见 JVM规范文档。搜索NoClassDefFoundError关键字,找到5处,搜ClassNotFoundException,也是5处。当然完整意义上来说,还得考察他们的父类,这里一切从简。


首先了解类的加载过程,见JVM规范的第5章,主要包括加载,链接,初始化(Chappter 5. Loading, Linking, and Initializing)。

看第5章第3节(5.3. Creation and Loading)

如果在类的Loading阶段发生错误,需要抛出LinkageError或者它的子类的异常,NoClassDefFoundError正好是它的子类,并且如果在验证或者解析阶段发生了ClassNotFoundException,虚拟机实现必须把它包装成NoClassDefFoundError。从文件中获取类的时候,如果文件的名实不副,会抛出这个Error。初始化阶段,如果类的状态不正确,也会抛出这个Error。


总的来说,这个NoClassDefFoundError是在类的加载,链接,验证,初始化阶段,由虚拟机抛出的,不像ClassNotFoundException,是由ClassLoader抛出的,可能是启动类加载器,也可能是用户定义的类加载器。简单理解,ClassNotFoundException是在ClassLoader加载类的时候,没有找到类文件抛出来的,而NoClassDefFoundError是在ClassLoader加载完类以后,虚拟机在加载,链接,验证,初始化过程抛出来的,需要注意ClassLoader加载和虚拟机加载的区别,ClassLoader加载完成以后,得到的只是一个Class类的对象,而这个对象还需要经过虚拟机的处理才能正常使用,不过这一步对程序员来说是看不见的。


接下来举个例子,比如在Eclipse中编写如下代码运行, 就会抛出{java.lang.ClassNotFoundException: A},但如果改成

Class c = cl.loadClass("classerror.A");,就能正常运行,因为这个地方需要传入Class文件的全限定名称,包括包名和类名,否则找不到。

package classerror;
public class TestClassError {
    public static void main(String[] args) {
        ClassLoader cl = new MyClassLoader();
        try {
            Class c = cl.loadClass("A");
            System.out.println(c);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
class A {
}
class MyClassLoader extends ClassLoader {
}


怎么模拟抛出一个NoClassDefFoundError呢,这个比较麻烦,我们用命令行进入代码目录,例如~/work/workspace/JavaTest/src/classerror,然后,执行

~/work/workspace/JavaTest/src/classerror$ javac *.java,会发现这个目录下面生成对应的class文件,和代码文件在同一个目录

A.class  MyClassLoader.class  TestClassError.class  TestClassError.java

然后cd .. 返回到src目录,执行

~/work/workspace/JavaTest/src$ java -classpath ~/work/workspace/JavaTest/src classerror.TestClassError

发现输出class classerror.A,表明一切正常,然后我们

~/work/workspace/JavaTest/src$ rm classerror/MyClassLoader.class 

删掉MyClassLoader.class 文件,再执行

~/work/workspace/JavaTest/src$ java -classpath ~/work/workspace/JavaTest/src classerror.TestClassError

发现抛出了Exception in thread "main" java.lang.NoClassDefFoundError: classerror/MyClassLoader

这个问题比较常见的情况是,借助三方的Jar文件编译成功的应用,没有把三方的Jar打包进来,然后运行的时候会出这个问题。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值