1. 概述
在这篇文章中,我们将简要介绍什么是异常,并深入探讨Java中的链式异常。
简单来说,异常是中断程序正常执行流程的事件。现在,让我们来看看如何利用链式异常来获取更好的语义。
2. 链式异常
链式异常有助于识别应用程序中一个异常导致另一个异常的情况。
例如,考虑一个方法,由于尝试除以零而抛出一个ArithmeticException
,但实际的异常原因是I/O错误导致了除数为零。该方法会将ArithmeticException
传递给调用者。调用者可能不知道异常的实际原因。在这种情况下,我们使用链式异常。
这个概念是在JDK 1.4中引入的。
接下来,我们将了解Java中如何支持链式异常。
3. Throwable
类
Throwable
类提供了一些构造函数和方法来支持链式异常。首先,我们来看一下构造函数:
Throwable(Throwable cause)
-Throwable
有一个参数,用于指定异常的实际原因。Throwable(String desc, Throwable cause)
- 这个构造函数接受异常描述以及异常的实际原因。
然后,让我们看看这个类提供的方法:
getCause()
方法 - 这个方法返回当前异常关联的实际原因。initCause()
方法 - 它设置底层原因,通过调用异常。
4. 示例
现在,让我们看一个示例,我们将设置自己的异常描述并抛出一个链式异常:
public class MyChainedException {
public void main(String[] args) {
try {
throw new ArithmeticException("Top Level Exception.")
.initCause(new IOException("IO cause."));
} catch(ArithmeticException ae) {
System.out.println("Caught : " + ae);
System.out.println("Actual cause: "+ ae.getCause());
}
}
}
如预期的那样,这将导致:
Caught: java.lang.ArithmeticException: Top Level Exception.
Actual cause: java.io.IOException: IO cause.
5. 为什么要使用链式异常?
我们需要链接异常来使日志更易读。让我们编写两个示例,一个不链接异常,另一个链接异常。然后,我们将比较两者在日志行为上的差异。
首先,我们将创建一系列的异常:
class NoLeaveGrantedException extends Exception {
public NoLeaveGrantedException(String message, Throwable cause) {
super(message, cause);
}
public NoLeaveGrantedException(String message) {
super(message);
}
}
class TeamLeadUpsetException extends Exception {
// Both Constructors
}
现在,让我们在代码示例中使用这些异常。
5.1. 不链接异常
让我们先写一个不链接自定义异常的示例程序。
public class MainClass {
public void main(String[] args) throws Exception {
getLeave();
}
void getLeave() throws NoLeaveGrantedException {
try {
howIsTeamLead();
} catch (TeamLeadUpsetException e) {
e.printStackTrace();
throw new NoLeaveGrantedException("Leave not sanctioned.");
}
}
void howIsTeamLead() throws TeamLeadUpsetException {
throw new TeamLeadUpsetException("Team Lead Upset");
}
}
在这个例子中,日志看起来像这样:
com.baeldung.chainedexception.exceptions.TeamLeadUpsetException:
Team lead Upset
at com.baeldung.chainedexception.exceptions.MainClass
.howIsTeamLead(MainClass.java:46)
at com.baeldung.chainedexception.exceptions.MainClass
.getLeave(MainClass.java:34)
at com.baeldung.chainedexception.exceptions.MainClass
.main(MainClass.java:29)
Exception in thread "main" com.baeldung.chainedexception.exceptions.
NoLeaveGrantedException: Leave not sanctioned.
at com.baeldung.chainedexception.exceptions.MainClass
.getLeave(MainClass.java:37)
at com.baeldung.chainedexception.exceptions.MainClass
.main(MainClass.java:29)
5.2. 链接异常
接下来,让我们写一个链接自定义异常的示例:
public class MainClass {
public void main(String[] args) throws Exception {
getLeave();
}
public getLeave() throws NoLeaveGrantedException {
try {
howIsTeamLead();
} catch (TeamLeadUpsetException e) {
throw new NoLeaveGrantedException("Leave not sanctioned.", e);
}
}
public void howIsTeamLead() throws TeamLeadUpsetException {
throw new TeamLeadUpsetException("Team lead Upset.");
}
}
最后,让我们看看带有链式异常的日志:
Exception in thread "main" com.baeldung.chainedexception.exceptions
.NoLeaveGrantedException: Leave not sanctioned.
at com.baeldung.chainedexception.exceptions.MainClass
.getLeave(MainClass.java:36)
at com.baeldung.chainedexception.exceptions.MainClass
.main(MainClass.java:29)
Caused by: com.baeldung.chainedexception.exceptions
.TeamLeadUpsetException: Team lead Upset.
at com.baeldung.chainedexception.exceptions.MainClass
.howIsTeamLead(MainClass.java:44)
at com.baeldung.chainedexception.exceptions.MainClass
.getLeave(MainClass.java:34)
... 1 more
我们可以很容易地比较这些日志,并得出结论,链式异常导致了更清晰的日志。
6. 总结
在这篇文章中,我们探讨了链式异常的概念。
所有示例的实现可以在GitHub项目中找到 - 这是一个基于Maven的项目,所以导入并运行它应该非常容易。