目录标题
- 前言
- 一、异常链介绍
- 1.1 异常链概述
- 1.2 Java中如何使用异常链
- 二、Throwable
- 1.1 Throwable中哪些API提供存储cause的功能
- 1.2 Throwable中如何获取cause
- 三、项目实战演练
- 示例1:未存储cause
- 示例2:存储cause两层嵌套
- 示例3:存储cause三层嵌套
- 四、总结
前言
“异常链”无论是在框架源码
中还是在日常项目开发
中,都是一项非常重要的对异常处理的手段。
一、异常链介绍
1.1 异常链概述
以下摘抄自《Java编程思想》对异常链的介绍:
常常会想要在
①捕获一个异常后抛出另一个异常
,②并且希望把原始异常的信息保存下来
,这被称为异常链。
1.2 Java中如何使用异常链
在JDK1.4以前,程序员必须自己编写代码来保存原始异常的信息。现在所有Throwable的子类在构造器中都可以接受一个cause(因由)对象作为参数
。这个cause就用来表示原始异常
,这样通过把原始异常传递给新的异常,使得即使在当前位置创建并抛出了新的异常,也能通过这个异常链追踪到异常最初发生的位置。
扩展:
有趣的是,在Throwable的子类中,只有三种基本的异常类提供了带cause参数的构造器。它们是Error(用于Java虚拟机报告系统错误)、Exception以及
RuntimeException。如果要把其他类型的异常链接起来,应该使用initCause()方法而不是构造器。
二、Throwable
对于Throwable的继承体系,以及旗下Exception划分编译异常、运行异常的讲解此处略。 此处,主要是对Throwable中cause属性(存储原始异常)的讲解。
1.1 Throwable中哪些API提供存储cause的功能
按照思路“*Throwable中的cause属性*
以及 *源码中肯定存在对cause属性的赋值操作*
”,以“this.cause = cause”作为条件,我们直接来到Throwable的源码中进行查找,我们发现有四个API可以提供了存储cause的行为:
public class Throwable implements Serializable {
private Throwable cause = this;
// ①
public Throwable(String message, Throwable cause) {
fillInStackTrace();
detailMessage = message;
this.cause = cause;
}
// ②
public Throwable(Throwable cause) {
fillInStackTrace();
detailMessage = (cause==null ? null : cause.toString());
this.cause = cause;
}
// ③
protected Throwable(String message, Throwable cause,
boolean enableSuppression,
boolean writableStackTrace) {
if (writableStackTrace) {
fillInStackTrace();
} else {
stackTrace = null;
}
detailMessage = message;
this.cause = cause;
if (!enableSuppression)
suppressedExceptions = null;
}
// ④
public synchronized Throwable initCause(Throwable cause) {
if (this.cause != this)
throw new IllegalStateException("Can't overwrite cause with " +
Objects.toString(cause, "a null"), this);
if (cause == this)
throw new IllegalArgumentException("Self-causation not permitted", this);
this.cause = cause;
return this;
}
}
1.2 Throwable中如何获取cause
有存储肯定有获取,Throwable中提供获取cause的API是:
/**
* Returns the cause of this throwable or {@code null} if the
* cause is nonexistent or unknown. (The cause is the throwable that
* caused this throwable to get thrown.)
*
* @return the cause of this throwable or {@code null} if the
* cause is nonexistent or unknown.
* @since 1.4
*/
public synchronized Throwable getCause() {
return (cause==this ? null : cause);
}
从注释“Returns the cause of this throwable or {@code null}”中,我们可以看到cause的值分为两种:
- this有值。即使用上述任意四种API存储了cause;
- null空值。即没有使用上述任意四种API。
因此我们在使用getCause()方法的时候,需要注意空处理。
三、项目实战演练
了解了cause相关的理论知识后,我们还必须通过代码的方式去验证我们的理论
,增强信服力
,加深印象
。
示例1:未存储cause
package com.atta.msgdactuator.zdemo;
import java.sql.SQLException;
import java.sql.SQLIntegrityConstraintViolationException;
/**
* @author: cms
* @date: 2023/1/7 9:03 PM
* @description: xxx
*/
public class TestMain {
public static void throwSQLIntegrityConstraintViolationException() throws SQLIntegrityConstraintViolationException {
throw new SQLIntegrityConstraintViolationException("exception-SQLIntegrityConstraintViolationException");
}
public static void throwRuntimeException() {
try {
throwSQLIntegrityConstraintViolationException();
} catch (SQLException e) {
throw new RuntimeException();
}
}
public static void main(String[] args) {
try {
throwRuntimeException();
}
catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
示例2:存储cause两层嵌套
package com.atta.msgdactuator.zdemo;
import java.sql.SQLException;
import java.sql.SQLIntegrityConstraintViolationException;
/**
* @author: cms
* @date: 2023/1/7 9:03 PM
* @description: xxx
*/
public class TestMain {
public static void throwSQLIntegrityConstraintViolationException() throws SQLIntegrityConstraintViolationException {
throw new SQLIntegrityConstraintViolationException("exception-SQLIntegrityConstraintViolationException");
}
public static void throwRuntimeException() {
try {
throwSQLIntegrityConstraintViolationException();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
try {
throwRuntimeException();
}
catch (Exception e) {
System.out.println(e.getCause());
}
}
}
通过调用e.getCause()获取到的是SQLIntegrityConstraintViolationException对象。
示例3:存储cause三层嵌套
package com.atta.msgdactuator.zdemo;
import java.sql.SQLException;
import java.sql.SQLIntegrityConstraintViolationException;
/**
* @author: cms
* @date: 2023/1/7 9:03 PM
* @description: xxx
*/
public class TestMain {
public static void throwSQLIntegrityConstraintViolationException() throws SQLIntegrityConstraintViolationException {
throw new SQLIntegrityConstraintViolationException("exception-SQLIntegrityConstraintViolationException");
}
public static void throwSQLException() throws SQLException {
try {
throwSQLIntegrityConstraintViolationException();
} catch (SQLIntegrityConstraintViolationException e) {
throw new SQLException("exception-SQLException", e);
}
}
public static void throwRuntimeException() {
try {
throwSQLException();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
try {
throwRuntimeException();
}
catch (Exception e) {
System.out.println(e.getCause());
}
}
}
四、总结
略。