Java Throws异常链怎么用
在 Java 里,异常链(Exception Chaining) 指的是:
在抛出一个新异常时,把“原始异常”作为原因(cause)保留下来,这样在排查问题时既能看到当前层的异常,又能追溯到根本原因。
下面分层说明怎么用。
一、为什么需要异常链
假设:
- DAO 层发生
SQLException - Service 层不想把底层细节暴露给上层
- 但又不想丢掉原始异常信息
✅ 正确做法:抛一个新的业务异常,把 SQLException 作为 cause 传进去
二、最常见用法(推荐)
1️⃣ 使用带 cause 的构造方法
try {
// 可能抛出 SQLException
} catch (SQLException e) {
throw new ServiceException("查询用户失败", e);
}
2️⃣ 自定义异常支持异常链
public class ServiceException extends RuntimeException {
public ServiceException(String message, Throwable cause) {
super(message, cause);
}
}
✅ 关键点:
super(message, cause)会把原始异常保存起来
三、异常链的核心 API
1️⃣ Throwable 提供的方法
Throwable getCause(); // 获取原始异常
Throwable initCause(Throwable cause); // 设置原因(较少用)
2️⃣ 打印异常链
try {
...
} catch (ServiceException e) {
e.printStackTrace();
}
输出示例:
ServiceException: 查询用户失败
at com.xxx.Service.query(Service.java:20)
Caused by: java.sql.SQLException: 连接失败
at com.xxx.Dao.query(Dao.java:15)
✅ Caused by 就是异常链的体现
四、异常链的几种写法
✅ 写法一(最推荐)
throw new ServiceException("业务失败", e);
✅ 写法二:先创建异常,再设置 cause(不常用)
ServiceException se = new ServiceException("业务失败");
se.initCause(e);
throw se;
⚠️ 一般只在 异常类没有带 cause 构造器 时才用
✅ 写法三:不包装,直接抛(不推荐)
catch (SQLException e) {
throw e; // 丢失业务语义
}
五、Checked / Unchecked 异常与异常链
场景 1:把 Checked 异常包装成 RuntimeException
try {
...
} catch (IOException e) {
throw new RuntimeException("文件读取失败", e);
}
✅ 很常见,避免方法签名污染
场景 2:继续抛出 Checked 异常
public void process() throws ServiceException {
try {
...
} catch (SQLException e) {
throw new ServiceException("处理失败", e);
}
}
六、异常链的最佳实践 ✅
-
不要吞掉异常
catch (Exception e) { // 空 catch ❌ } -
不要只抛 message,不传 cause
throw new ServiceException("失败"); // ❌ 原始异常丢失 -
异常链要完整
Controller → ServiceException → SQLException -
异常类型要有业务语义
UserServiceExceptionOrderProcessException- 而不是通篇
RuntimeException
七、一句话总结
Java 异常链 = 新异常 + 原始异常(cause)
用new XxxException(message, cause)让异常既有业务含义,又保留根因。
如果你愿意,我也可以帮你:
- 设计一个 异常体系
- 分析 Spring / 项目里异常链的真实案例
- 解释 Throwable、Exception、Error 的关系