捕获后重新抛出必须用带cause参数的构造函数包装原始异常,以保留堆栈、类型、消息及嵌套原因;常见错误包括丢弃原始异常或仅传消息,正确做法是throw new serviceexception("描述", e),确保日志和调试时可追溯完整因果链。

关键就一条:捕获后重新抛出时,必须用带 cause 参数的构造函数包装,而不是新建一个“干净”的异常。
别吞掉原始异常,也别重造一个空壳
原始异常里藏着堆栈、类型、消息和可能的嵌套原因——这些是定位根因的唯一线索。常见错误包括:
- 只写
throw new ServiceException("操作失败"):丢失全部原始信息 - 写
throw new Exception(e.getMessage()):堆栈重置为当前行,getCause()为空 - 捕获后仅
log.error("", e)然后静默返回:异常消失,调用链中断
正确封装:一行代码完成链式传递
在 catch 块中,直接使用标准异常类或自定义异常的双参构造器:
throw new ServiceException("文件读取失败", e);throw new DaoException("用户查询异常", sqlEx);throw new NestedRuntimeException("配置加载失败", e);
这样构造的新异常,e.getCause() 就指向原始异常,printStackTrace() 会完整输出“Caused by”嵌套链。
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
多层调用中确保链路不断
每层都应遵循同一原则:不拦截、不丢弃、只增强语义。例如:
- DAO 层捕获
SQLException→ 包装为DaoException并传入cause - Service 层捕获
DaoException→ 包装为BusinessException,仍传入该异常作为cause - Controller 层不 catch,由全局
@ExceptionHandler统一处理
最终日志里能看到从 BusinessException → DaoException → SQLException → SocketTimeoutException 的完整因果链。
日志和调试时怎么看到完整链
打印必须用原异常对象,不能只打消息:
- ✅ 正确:
log.error("转账失败", ex);(SLF4J 自动展开整个 cause 链) - ❌ 错误:
log.error("转账失败: {}", ex.getMessage());(只剩顶层消息) - 调试时,在 IDE 中点开异常变量,展开
cause字段可逐层查看
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










