ClassNotFoundException 和 NoClassDefFoundError 的区别

Java 面试中有个很常见的问题:请对比 Exception 和 Error,另外一个,“运行时异常” 与 “一般异常” 有什么区别?

今天在看 「极客时间」的 《Java 核心技术 36 讲》 中又有提到。

大家都熟悉的典型回答:

  1. Exception 和 Error 都是继承了 Throwable 类,在 Java 中只有 Throwable 类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。
  2. Exception 和 Error 体现了 Java 平台设计者对不同异常情况的分类。Exception 是程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相应处理。
  3. Error 是指在正常情况下,不大可能出现的情况,绝大部分的 Error 都会导致程序(比如 JVM 自身)处于非正常的、不可恢复状态。既然是非正常情况,所以不便于也不需要捕获,常见的比如 OutOfMemoryError 之类,都是 Error 的子类。
  4. Exception 又分为可检查(checked)异常和不检查(unchecked)异常,可检查异常在源代码里必须显式地进行捕获处理,这是编译期检查的一部分。前面我介绍的不可查的 Error,是 Throwable 不是 Exception。
  5. 不检查异常就是所谓的运行时异常,类似 NullPointerException、ArrayIndexOutOfBoundsException 之类,通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译期强制要求。

在杨老师分析这道题时,有提到,关于异常需要掌握的两个方面:

这里提出了一个问题:ClassNotFoundException 和 NoClassDefFoundError 有什么区别

在这之前,自己没有遇到过这个问题,就借此机会做下思考:

ClassNotFoundException 和 NoClassDefFoundError

首先,正如它们的名字所说明的:

NoClassDefFoundError是一个错误(Error),而 ClassNOtFoundException 是一个异常

在Java中错误和异常是有区别的:我们可以从异常中恢复程序,但却不应该尝试从错误中恢复程序。

ClassNotFoundException 产生的原因:

Java支持使用 Class.forName 方法来动态地加载类,任意一个类的类名如果被作为参数传,递给这个方法都将导致该类被加载到 JVM 中。如果这个类在类路径中没有被找到,那么此时就会在运行时抛出 ClassNotFoundException 异常。

要解决这个问题,就要确保所需的类连同它依赖的包存在于类路径中。当 Class.forName 被调用的时候,类加载器会查找类路径中的类,如果找到了那么这个类就会被成功加载,如果没找到,那么就会抛出ClassNotFountException。

除了 Class.forName,ClassLoader.loadClass、ClassLOader.findSystemClass 在动态加载类到内存中的时候也可能会抛出这个异常。

另外还有一个导致 ClassNotFoundException 的原因就是:当一个类已经某个类加载器加载到内存中了,此时另一个类加载器又尝试着动态地从同一个包中加载这个类。

由于类的动态加载在某种程度上是被开发者所控制的,所以他可以选择 catch 这个异常然后采取相应的补救措施。

NoClassDefFoundError 产生的原因:

当 Java 虚拟机 或 ClassLoader 实例试图在类的定义中加载(作为通常方法调用的一部分,或者是使用 new 来创建新的对象)时,却找不到类的定义(要查找的类在编译的时候是存在的,运行的时候却找不到了),抛出此异常。

即当前执行的类被编译时,所搜索的类定义存在,但无法再找到该定义。

这个错误往往是你使用 new 操作符来创建一个新的对象,但却找不到该对象对应的类。这个时候就会导致NoClassDefFoundError

由于 NoClassDefFoundError 是有 JVM 引起的,所以不应该尝试捕捉这个错误。

解决这个问题的办法就是:查找那些在开发期间存在于类路径下但在运行期间却不在类路径下的类

二者的区别

ClassNotFoundException 发生在装入阶段。

当应用程序试图通过类的字符串名称,使用常规的三种方法装入类,但却找不到指定名称的类定义时就抛出该异常。

NoClassDefFoundError 当目前执行的类已经编译,但是找不到它的定义时。也就是说你如果编译了一个类B,在类A中调用,编译完成以后,你又删除掉B,运行A的时候那么就会出现这个错误。

加载时从外存储器找不到需要的 Class 就出现 ClassNotFoundException

连接时从内存找不到需要的 class 就出现 NoClassDefFoundError

写在最后:

异常的出现让我们编写的程序运行起来更加的健壮,同时为程序在调试、运行期间发生的一些意外情况,提供了补救机会; 不要推诿或延迟处理异常,就地解决最好,并且需要实实在在的进行处理,而不是只捕捉,不动作, 如无必要,勿用异常。

关于异常的处理,留一个思考题给大家:

下面的代码反映了异常处理中哪些不当之处?欢迎留言讨论。

1try {
2  // 业务代码
3  // …
4  Thread.sleep(1000L);
5} catch (Exception e) {
6  // Ignore it
7}

原文发布于微信公众号 - 程序员阿凯(AKBC159)

原文发表时间:2018-05-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏技术栈大杂烩

Python: 受限制的 "函数调用"

函数功能简单明了, 对于结果, 大家应该也不会有太大的异议:func分别是取得全局命名空间中a的值和使用内置命名空间中的函数id获取了a的地址. 熟悉Pytho...

1373
来自专栏十月梦想

ES6语法基础之let用法

简单讲解一些ES6语法基础!了解一些es6新特性!当然下一步需要学习的vue框架也是基于es6的,因此很有必要学习下es6语法,接下来几次简单讲解es6语法!

863
来自专栏web前端教室

复习知识点 -- JS高效拼接字符串

image.png JS拼接字符串,一种是用变量 += ,另一种是用 join。这个办法我早就知道,但一直用的不是很熟练,今天就复习一下。 第一是js的字符...

27210
来自专栏java学习

java基础笔记1

Java基础 | 数据库 | Android | 学习视频 | 学习资料下载 最新通知 按照我去培训机构的学习经历,给初学还有自学Java 的同学一个基本的学习...

3537
来自专栏猿人谷

C++内存管理学习堆和栈

一 C++内存管理 1.内存分配方式   在讲解内存分配之前,首先,要了解程序在内存中都有什么区域,然后再详细分析各种分配方式。 1.1 C语言和C++内存分...

2926
来自专栏PHP在线

PHP 底层的运行机制与原理

原文出处: nowamagic 欢迎分享原创到伯乐头条 PHP说简单,但是要精通也不是一件简单的事。我们除了会使用之外,还得知道它底层的工作原理。 PHP是...

3607
来自专栏java思维导图

Java 10 已发布!时隔 6 月带来 109 项新特性

关键时刻,第一时间送达! 期待已久,没有跳票的 Java 10 已正式发布! ? 为了更快地迭代,以及跟进社区反馈,Java 的版本发布周期变更为了每六个月一次...

2847
来自专栏沈唁志

基于ThinkPHP中App(通信)接口开发封装JSON数据 并读取JSON数据的封装

7202
来自专栏冰霜之地

深入浅出 FlatBuffers 之 Schema

FlatBuffers 是一个序列化开源库,实现了与 Protocol Buffers,Thrift,Apache Avro,SBE 和 Cap'n Proto...

2362
来自专栏Petrichor的专栏

python: 下划线 使用

此时“_”、“__”、“___”、“____”等等作为临时性的名称使用。这样,当其他人阅读你的代码时将会知道,你分配了一个特定的名称,但是并不会在后面再次用到该...

1443

扫码关注云+社区