应使用带cause参数的构造函数包装异常,如throw new runtimeexception("处理订单失败", originalexception),避免仅拼接getmessage()丢失堆栈;自定义异常需提供super(message, cause)构造器;日志要传异常对象而非字符串。

在包装新异常时保留原始异常的堆栈轨迹,关键在于正确使用异常链机制——即把原始异常作为 cause 传入新异常的构造函数,而不是仅靠 getMessage() 拼接字符串。
用带 cause 参数的构造函数
大多数标准异常(如 RuntimeException、IOException 等)都提供接受 Throwable cause 的构造方法。这是最直接、最可靠的方式:
- ✅ 正确:
throw new RuntimeException("处理订单失败", originalException); - ❌ 错误:
throw new RuntimeException("处理订单失败: " + originalException.getMessage());(丢失堆栈、压制 cause)
避免手动打印或吞掉原始异常
不要在日志中只记录 e.toString() 或 e.getMessage() 后就抛出新异常;也不要捕获后不做任何处理(即“吞异常”):
- 日志应调用
logger.error("操作失败", e);(传入异常对象,而非字符串) - 若需补充上下文,仍应保留 cause:
throw new ServiceException("支付回调验证失败", e);
自定义异常要支持 cause 链
如果定义了自己的异常类,务必提供接收 Throwable cause 的构造函数,并调用父类对应构造器:
- 示例:
public class ValidationException extends RuntimeException {<br> public ValidationException(String message, Throwable cause) {<br> super(message, cause); // 关键:转发给父类<br> }<br>} - 缺少该构造函数,或未调用
super(message, cause),会导致链断裂。
注意 try-with-resources 和多层包装的陷阱
在资源自动关闭或嵌套异常包装场景中,容易意外覆盖 cause:
- try-with-resources 中若
close()抛异常,且已有异常在传播,JVM 会自动将 close 异常添加为 suppressed,但主异常的 cause 不受影响——前提是没手动重抛错误的新异常。 - 连续包装时(A → B → C),确保每一层都正确传入上一层异常作为 cause,否则中间链会断开。










