浅析 ClassNotFoundException 与 NoClassDefFoundError

最近接到电话面试中,面试官问我 ClassNotFoundException 与 NoClassDefFoundError 有什么样的区别,至于前者经常遇到,我还是能答上一些,至于后者,完全没遇到过啊,只好瞎编了,都忘记当时是怎么回答的。然后面试官问我他们都会出现在哪些情况,ClassNotFoundException 我还可以说出几种情况,可是后者这下不能编了,只好求饶。结果面试官还给我讲解了一下,感觉非常尴尬。 回来以后又查阅了一些相关的资料,并尝试了一下,整理分享给网友。

ClassNotFoundException

这个异常我们遇到的频率还是很高的,先来看看官方文档中的定义:

当应用程序试图使用以下方法通过字符串名加载类时,抛出该异常:

  • Class 类中的 forName 方法。
  • ClassLoader 类中的 findSystemClass 方法。
  • ClassLoader 类中的 loadClass 方法。

但是没有找到具有指定名称的类的定义。 从 1.4 版本开始,此异常已经更新,以符合通用的异常链机制。在构造时提供并通过 getException() 方法访问的 “加载类时引发的可选异常”,现在被称为原因,它可以通过 Throwable.getCause() 方法以及与上面提到的 “遗留方法” 来访问。

也就是说在类加载阶段,通过类全名加载类时,如果不能找到这个类,就会抛出 ClassNotFoundException,当然,通常在开发中可能出现该异常的情况总结如下:

  1. 所需要的支持类库放错了地方,并没有放在类路径 (CLASSPATH 环境变量) 里面。
  2. 使用了重复的类库,且版本不一致。导致低版本的被优先使用。
  3. 类名错了,一般是使用 Class.forName 的时候,手工指定了类名的情况。
  4. 没有导入纯 JAVA 驱动包。

当然啦,我经常遇到这个异常,尤其是在使用 JDBC 加载驱动的时候,驱动类的类全名真的是很难记的有木有!

NoClassDefFoundError

可以看到这个错误的类名以 Error 结尾,因此它属于 JVM 虚拟机错误,不需要程序员捕获,下图是它的继承结构:

还是同样先来看下官方文档中的定义:

当 Java 虚拟机或 ClassLoader 实例试图在类的定义中加载(作为通常方法调用的一部分或者作为使用 new 表达式创建的新实例的一部分),但无法找到该类的定义时,抛出此异常。 当前执行的类被编译时,所搜索的类定义存在,但无法再找到该定义。

通俗的说,就是编译的时候有这个类,而运行期的时候,这个类找不到了!就会抛出 NoClassDefFoundError 异常。 想要演示这个异常的出现很简单,只需要在编译好的 class 文件中将对应类的 class 文件删除,运行的时候就会抛出这个异常啦! 那么在实际开发中,通常会在哪些情况遇到该异常呢?通常我们在开发过程中,肯定不会刻意的去删除某个 class 文件,但仍然有出现该异常的情况。 比如我们在使用第三方 jar 包时,有些 SDK 也会设定自己的 Classpath。编译过程结束后在运行的过程中就要将已开发的应用和所有引入的 jar 包拷贝到应用服务器的相应目录下才可以运行,而应用服务器使用的 Classpath 也很有可能与 SDK 的不同,在这个过程中就有很大的几率造成双方环境不一致。所以很多开发者就会遇到在 SDK 中可以编译,运行也没有问题,但是同样的程序放到应用服务器上就出现 NoClassDefFoundErr 这个异常这种情况。

参考文章: 1. JavaTM Platform Standard Edition 6 的 API 规范 2. NoClassDefDoundErr 与 ClassNotFoundException 区别