getCause() 直接返回 cause 字段的原始引用,不进行任何拷贝;它既非深拷贝也非浅拷贝,调用方获得的是同一对象实例,修改其可变状态会影响所有引用处。

getCause() 不返回拷贝,只返回原始引用。它既不是深拷贝,也不是浅拷贝——它根本没做任何拷贝操作。
getCause() 的本质是直接返回字段引用
Java 中的 Throwable 类通过一个私有字段 cause 存储异常原因(比如调用 initCause() 或在构造时传入)。getCause() 方法只是简单地返回这个字段的值:
- 如果
cause是null,就返回null; - 否则,原封不动返回该异常对象的引用,不新建对象,也不复制内容。
这意味着:调用方拿到的是和原始异常中完全相同的那个对象实例,内存地址一致,修改其可变状态(如通过反射或暴露的 setter)会影响所有持有该引用的地方。
为什么不存在“getCause 返回深/浅拷贝”的需求
异常对象的设计初衷是**不可变性优先**:
立即学习“Java免费学习笔记(深入)”;
- 核心字段(message、stackTrace、cause)通常在构造时设定,后续不提供公开的修改方法;
- 即使子类扩展了可变字段,标准异常链也不鼓励运行时修改 cause 对象;
- 异常用于传递错误上下文,不是数据载体,无需隔离修改。
因此,JDK 没有为 getCause() 添加拷贝逻辑,也不建议自行包装成深拷贝——这反而可能掩盖问题或破坏调试信息(如堆栈跟踪丢失)。
如果你真需要独立副本,得自己实现
若因特殊场景(如日志脱敏、跨线程安全传递)必须隔离 cause 异常,需手动深拷贝。常见方式包括:
- 序列化反序列化:要求 cause 类及其所有字段都可序列化,是最通用的深拷贝手段;
-
手动重建:用
getCause().getMessage()和getCause().getStackTrace()构造新异常,但会丢失类型信息和自定义字段; -
第三方库:如 Apache Commons Lang 的
ExceptionUtils.clone()(基于序列化)。
注意:这些都不是 getCause() 的行为,而是你额外加的封装逻辑。
不复杂但容易忽略:别把“获取引用”误解为“生成副本”。


















