直接用 throw e; 最省事安全,保留原始堆栈;转业务异常须用双参构造(message + cause);日志记录必须传整个异常对象,不可拼字符串;finally 中 throw 或 return 会吞异常,initcause() 和 runtimeexception(e) 单参构造均破坏堆栈链。
直接用 throw e; 最省事也最安全,原始堆栈从最初抛出处开始,中间 catch 行不会插入新帧;需要转成业务异常时,必须用双参构造(message + cause),日志记录要传整个异常对象,不能拼字符串。
原样重抛:不新建对象,堆栈零损耗
捕获后若只需向上透传、不做语义转换,就别 new 新异常。直接写 throw e; 即可。
- 方法签名里得声明 throws 对应类型,否则编译失败
- 日志想加又不破坏堆栈?用 logger.error("读取失败", e),不是 "读取失败: " + e
- try-with-resources 中主异常仍走这条路径,关闭异常会自动进 getSuppressed(),不影响主链
包装为业务异常:cause 必须显式传入
把 SQLException、IOException 这类技术异常转成 UserRegisterException、OrderProcessFailedException 等带业务含义的异常时,关键动作是把原始异常作为 cause 传进去。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 正确写法:throw new UserRegisterException("注册失败", e);
- e 必须是当前 catch 块中捕获的那个变量,不能为 null
- 自定义异常类的构造函数里,要调用 super(message, cause),漏掉这步 getCause() 就返回 null
- 错误写法:throw new UserRegisterException("DB error: " + e.getMessage());——cause 为空,堆栈断链
日志和调试:看到完整链路才算真正保留
代码里链建对了,日志打错照样白搭。
- 记录日志必须传整个异常对象:log.error("下单异常", ex)
- 确认日志框架配置支持全堆栈输出:Logback 用 %ex,Log4j2 用 %throwable{full}
- 调试时别只看 ex.getCause(),有时原始异常藏在 ex.getSuppressed() 里(尤其 try-with-resources 场景)
- 最可靠方式是直接调 ex.printStackTrace(),里面有 “Caused by” 和 “Suppressed” 分段
避开几个高危陷阱
这些操作表面看着像在处理异常,实则悄悄截断了堆栈链。
- finally 里 throw 或 return:会劫持出口,吞掉 try/catch 中的原始异常
- 用 initCause():只能调一次,且仅对 cause 为 null 的异常生效,现代代码基本不用
- RuntimeException(e) 单参构造:JDK 把 e 当 message 处理,不是 cause,堆栈起点丢失
- catch 后空着不 throw:异常被吞,上层完全感知不到问题
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










