“全栈2019”Java异常第十五章:异常链详解

2,305 阅读9分钟

难度

初级

学习时间

30分钟

适合人群

零基础

开发语言

Java

开发环境

  • JDK v11
  • IntelliJ IDEA v2018.3

友情提示

  • 本教学属于系列教学,内容具有连贯性,本章使用到的内容之前教学中都有详细讲解。
  • 本章内容针对零基础或基础较差的同学比较友好,可能对于有基础的同学来说很简单,希望大家可以根据自己的实际情况选择继续看完或等待看下一篇文章。谢谢大家的谅解!

1.异常链的重要性

异常链非常重要,在我们日常开发中基本上都会碰到。学会看懂异常链是一项必备技能,它能够让你在调试大型应用时处理异常更加容易。

通俗来讲就是,小伙伴们,以后使用框架开发时,别人报的错都是异常链,你看不懂很难找到真正出错的地方。

2.重新抛出异常

首先,请大家思考一个问题:人和人直接通过手拉手可以组成一个人链,那么异常和异常之间能否通过某种关系组成异常链?

答案是可以的,具体是什么方法使多个异常之间组成异常链下面会讲解到。现在注意一个用词“异常和异常之间”,说明有多个异常才能组成异常链。

但是我们知道,一个方法里面有多处抛异常时,当某一处发生异常时,其他异常就不会发生。什么意思呢?来看一段程序:

“全栈2019”Java异常第十五章:异常链详解

这段程序中,我们可以看到抛出了两个异常:

“全栈2019”Java异常第十五章:异常链详解

如果第一个异常被抛出:

“全栈2019”Java异常第十五章:异常链详解

那么下面这个异常就不会被抛出:

“全栈2019”Java异常第十五章:异常链详解

也就是说我们的异常链不是将在同一个try代码块里面的异常连接起来

那么请大家再接着思考一个问题:如果异常链不是将try代码块里面的异常连接起来,那么会不会是这样的,我们抛出第一个异常,然后我们捕获到了第一个异常,在catch代码块里面处理该异常时,又抛出了新异常,这个新异常就是第二个异常,再通过某种方式将第一个异常和第二个异常连接起来,于是我们的异常链就做起来了,对不对?

对的,就是这么个思路。好,我们来化繁为简:抛出异常=》捕获异常=》抛出新异常

改写上面程序:

“全栈2019”Java异常第十五章:异常链详解

好像有点不对。

哪里不对?

上图中的IOException和ClassNotFoundException并没有关联起来,这不能称作是异常链。

下面我们就来学习如何构造异常链。

3.异常链

异常链的定义:

捕获一个异常后抛出另一个异常,并且把原始异常信息保存下来,这被称为异常链。

第一句,捕获一个异常后抛出另一个异常

这句话的意思是我们使用try-catch捕获到异常后,在catch代码块里面处理异常时抛出新异常。

第二句,并且把原始异常信息保存下来

原始异常是指捕获到的那个异常。

保存下来的意思是说通过传递参数的方式将其捕获到的异常对象传递进新异常对象里面。还不理解的小伙伴可以结合下面例子再来看看。

构造异常链很简单,就是通过Throwable类及其子类的构造方法来完成的,具体是哪个方法呢,请看:

Throwable​(String message, Throwable cause)

第一个参数,message

该参数是String字符串类型,可以存储一些有关该异常的详细信息,换言之就是给出该错误或者异常的信息或原因。那么以后我们呢,可以通过Throwable的getMessage()方法来获取错误信息或原因。

第二个参数,cause

该参数是Throwable类型,说明它可以存储上一个Throwable对象(你也可以理解为异常对象),cause翻译过来就是原因的意思,顾名思义,你可以将错误原因(Throwable对象)继续向后传递。

该参数还可以为空,表示没有原因(Throwable对象),也就是说你可以不传递Throwable对象。

好了,该解释的都解释了,接下来我们来用用看。

演示:

请构造一个异常链。

请观察程序代码及结果。

代码:

Main类:

“全栈2019”Java异常第十五章:异常链详解

结果:

“全栈2019”Java异常第十五章:异常链详解

程序运行结果我们先放一边,先来看程序代码。

我们将之前写在main()方法里面的代码移到了a()方法里面:

“全栈2019”Java异常第十五章:异常链详解

为什么要这么做?

因为我们要有一个地方捕获到新抛出的异常,所以我们的做法是一个方法抛一个方法捕获。在这个例子中,a()方法负责抛,main()方法负责捕获。

a()方法里面还有一处重要的地方,那就是抛出新异常,构造异常链

“全栈2019”Java异常第十五章:异常链详解

后面第二个参数就是catch参数e。

接着就是在main()方法里面调用了a()方法:

“全栈2019”Java异常第十五章:异常链详解

因为a()方法里面可以产生异常,而且这个异常还是非运行时异常,所以要用try-catch或throws做处理,我们选择的是try-catch。

接着,我们在catch里面打印了异常跟踪堆栈信息:

“全栈2019”Java异常第十五章:异常链详解

执行结果就是:

“全栈2019”Java异常第十五章:异常链详解

文字版:

java.lang.ClassNotFoundException: IO流错误at main.Main.a(Main.java:34)at main.Main.main(Main.java:14)Caused by: java.io.IOExceptionat main.Main.a(Main.java:30)... 1 more

其中“IO流错误”是我们在构造异常链时写的:

“全栈2019”Java异常第十五章:异常链详解

“IO流错误”也是ClassNotFoundException产生的原因。

at main.Main.a(Main.java:34)at main.Main.main(Main.java:14)

上面这两行反映的是发生ClassNotFoundException异常时的方法调用链:

“全栈2019”Java异常第十五章:异常链详解

Caused by: java.io.IOException

这一行表示的是发生ClassNotFoundException异常的原因,就是为什么会发生ClassNotFoundException异常。

Caused by: java.io.IOExceptionat main.Main.a(Main.java:30)

上面第二行反映的是IOException发生时的方法调用链。

整个异常链的构造非常简单,但是往往就是因为这么一个小知识点,多少人不知道去解决大型应用中的错误,就是因为他们很多人根本就不知道有异常链的存在,所以希望大家能够好好体会一些异常链的应用,能够熟练掌握它。

4.获取错误/异常原因

刚刚我们知道,我们构造异常链时:

Throwable​(String message, Throwable cause)

第二个参数就是错误/异常原因。

我们可以通过一个方法来单独获取它:

public Throwable getCause()

这个方法返回的是此Throwable的原因,如果原因不存在或未知,则返回null。

这里的“此Throwable”指的是?

比如上例中的ClassNotFoundException:

“全栈2019”Java异常第十五章:异常链详解

简单来讲就是产生新异常的原因

还不懂,没关系,再换一种说法,就是因捕获到的异常而产生的新异常

像上例中,“因捕获到的异常”指的是IOException。“而产生的新异常”指的是ClassNotFoundException。

getCause()方法还可能返回null,为什么呢?因为有些异常的产生是没有受之前的异常所影响的,所以原因为空。

说了这么多,我们来试试这个方法。

演示:

请使用getCause()方法获取异常原因。

请观察程序代码及结果。

代码:

Main类:

“全栈2019”Java异常第十五章:异常链详解

结果:

“全栈2019”Java异常第十五章:异常链详解

我们先来看程序代码,再来看程序结果。

此次程序代码和上一小节的代码只有一个地方存在不同:

“全栈2019”Java异常第十五章:异常链详解

catch代码块里面不同。此次程序中,我们先使用getCause()方法获取异常原因,也就是Throwable对象:

“全栈2019”Java异常第十五章:异常链详解

然后调用Throwable对象的printStackTrace()方法来打印跟踪异常堆栈信息:

“全栈2019”Java异常第十五章:异常链详解

其执行结果就是:

“全栈2019”Java异常第十五章:异常链详解

文字版:

java.io.IOExceptionat main.Main.a(Main.java:32)at main.Main.main(Main.java:14)

我们可以看到的就是这就是产生IOException异常的信息。

这就是我们通过getCause()方法来获取的异常原因,返回值来自异常链中所存储的Throwable对象。

5.多个异常的异常链

上面我们演示了两个异常组成的异常链,下面我们来看看多个异常组成的异常链是什么样的呢。

注意:

本小节只做演示,不做具体分析,因为异常链已经在上面小节中做了分析,本小节仅仅只是添加了多个异常对象而已,所以没有必要重复分析。

演示

请构造多个异常的异常链。

请观察程序代码及结果。

代码:

Main类:

“全栈2019”Java异常第十五章:异常链详解

结果:

“全栈2019”Java异常第十五章:异常链详解

文字版:

java.io.IOException: a方法产生异常

at main.Main.a(Main.java:30)

at main.Main.main(Main.java:14)

Caused by: java.lang.ClassNotFoundException: b方法产生异常

at main.Main.b(Main.java:47)

at main.Main.a(Main.java:27)

... 1 more

Caused by: java.io.IOException

at main.Main.b(Main.java:43)

... 2 more

总结

  • 异常链非常重要,在我们日常开发中基本上都会碰到。学会看懂异常链是一项必备技能,它能够让你在调试大型应用时处理异常更加容易。
  • 我们抛出第一个异常,然后我们捕获到了第一个异常,在catch代码块里面处理该异常时,又抛出了新异常,这个新异常就是第二个异常,再通过某种方式将第一个异常和第二个异常连接起来,于是我们的异常链就做起来了。
  • 捕获一个异常后抛出另一个异常,并且把原始异常信息保存下来,这被称为异常链。

至此,Java中异常链相关内容讲解先告一段落,更多内容请持续关注。

答疑

如果大家有问题或想了解更多前沿技术,请在下方留言或评论,我会为大家解答。

上一章

“全栈2019”Java异常第十四章:将异常输出到文本文件中

下一章

“全栈2019”Java异常第十六章:Throwable详解

学习小组

加入同步学习小组,共同交流与进步。

  • 方式一:关注头条号Gorhaf,私信“Java学习小组”。
  • 方式二:关注公众号Gorhaf,回复“Java学习小组”。

全栈工程师学习计划

关注我们,加入“全栈工程师学习计划”。

“全栈2019”Java异常第十五章:异常链详解

版权声明

原创不易,未经允许不得转载!