深入理解NoClassDeFoundError与ClassNotFoundException

1.先看一下两个类的继承结构图

  • NoClassDeFoundError
    深入理解NoClassDeFoundError与ClassNotFoundException_第1张图片

  • ClassNotFoundException
    深入理解NoClassDeFoundError与ClassNotFoundException_第2张图片

通过对比这两张图就会发现他们的区别在于,一个继承自Exception,另一个继承自Error
这里就涉及到Exception与Error的区别了。
Exception属于CheckedException错误,就是程序能捕获处理的错误。
而Error属于UnCheckedException,是程序无法捕获处理的错误。

2.NoClassDeFoundError

  • 根据上面的继承结构图我们可以发现NoClassDeFoundError继承自LinkageError,根据名字我们就可以猜到这是一个类加载过程中连接时错误。这里涉及到JVM中类加载的过程

  • 类的生命周期图
    深入理解NoClassDeFoundError与ClassNotFoundException_第3张图片
    在Linking的解析阶段:解析阶段是JVM将常量池内的符号引用替换为直接引用的过程
    解释一下符号引用直接引用

  • 符号引用:就是用一组符号来描述所引用的目标,符号引用可以是任何形式的字面量,只要使用时能无歧义的定位到目标即可。

  • 直接引用: 就是直接指向目标的指针

这两句话有的人可能不太理解,举个例子

public static void main(String[] args) {
      new ArrayList();
  }

比如里面的new ArrayList(),在类生命周期的解析阶段之前,还只是符号这就是符号引用。 在解析过程中,JVM会去找ArrayList是否被加载,如果未加载会先加载ArrayList,然后返回ArrayList的引用。这就是直接引用

所以,如果在连接阶段的解析时,找不到相应的类,就会报NoClassDeFoundError
这里我们举个NoClassDeFoundError的例子(话说这个例子还真不好找啊o(╥﹏╥)o)

注意,以下例子只会在Windows报错!只会在Windows报错!只会在Windows报错!重要的事情说三遍,别你在Linux上运行没有报错来找我 ̄□ ̄||(具体原因在下面讲)

public class Main {
    public static void main(String[] args) {
        ABC abc=new ABC();
    }
}
class ABC{

}
class abc extends ABC{

}

报错信息

Exception in thread "main" java.lang.NoClassDefFoundError
 ...(此处省略)

为什么会报错呢,最开始我也百思不得其解,后经一位大神一语道破了天机。豁然开朗。
在Windows中,文件名不区分大小写
所以,在编译的时候,先编译父类生成ABC.class然后编译子类abc.class,这个时候由于不区分大小写,abc.class直接覆盖掉了ABC.class的内容了。不信的话,看图
深入理解NoClassDeFoundError与ClassNotFoundException_第4张图片

然后在 解析 阶段的时候,就找不到ABC这个类啦~。所以就报 NoClassDeFoundError 咯~~
(Linux上我已经测试过了,编译后文件内容不会覆盖,这里就不放图了,追求真理的孩子自己去尝试吧~)

3.ClassNotFoundException

这个这个错误其实是经常遇到的。比如JDBC的时候忘了添加驱动。
呐~直接举个例子

public static void main(String[] args) throws ClassNotFoundException {
   Class.forName("com.mysql.jdbc.driver");
}

报错信息

Exception in thread "main" java.lang.ClassNotFoundException: com.mysql.jdbc.driver
···(此处省略)

细心的同学可能发现了在使用Class.forName();必须要抛出异常或者try/catch。这也是就Excetion与Error的区别。一个是能捕获到的,一个是不能捕获到的错误。
这里的应该是程序运行的时候通过类的全限定名去加载类的时候找不到这个类抛出的异常。

4.最后再总结一下:

  • NoClassDeFoundError 继承自Error属于用户程序无法捕获处理的异常

  • ClassNotFoundException 继承自Exception属于用户程序能捕获处理的异常

  • NoClassDeFoundError 发生在类生命周期中解析阶段找不到相应的类

  • ClassNotFoundException 发生在类生命周期的加载阶段,找不到相应的类。

以上内容根据资料分析并实践所得,如有错误,请大家批评指正~

参考资料
《深入理解Java虚拟机:JVM高级特性与最佳实践》周志明

你可能感兴趣的:(JVM,JavaSE)