Java原生序列化支持循环引用,不会导致StackOverflowError;问题多出自JSON库、toString()、深拷贝等非原生场景,需通过引用跟踪、忽略字段或DTO转换解决。

Java 原生序列化(java.io.Serializable)本身不会因循环引用导致 StackOverflowError,它内置句柄表机制自动识别重复对象并写入引用标记;真正出问题的,通常是 JSON 库、手动深拷贝、toString() 或其他非原生序列化场景。
确认是不是 Java 原生序列化在报错
先看堆栈:如果错误出现在 java.io.ObjectOutputStream 或 ObjectInputStream 中,且类都实现了 Serializable,那基本不是原生机制的问题——它天生支持循环引用。更可能是:
- 某个字段被声明为
transient,但反序列化后逻辑误判为“未初始化”而触发递归重建 - 自定义了
writeObject方法却没调用defaultWriteObject(),导致绕过句柄表逻辑 - 类里有非
Serializable的成员(比如某些第三方类),抛的是NotSerializableException,但被异常处理掩盖成栈溢出
JSON 库(Jackson/Fastjson/Gson)是重灾区
这些库默认按树形结构递归展开,不开启引用跟踪就会无限深入。常见解法:
-
Jackson:加
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)到类上,或全局启用objectMapper.enableDefaultTyping() -
Fastjson:1.2.83+ 版本默认支持
$ref;旧版本需显式设置SerializerFeature.DisableCircularReferenceDetect = false -
Gson:用
GraphAdapterBuilder(官方 extras 包),它把对象图转成 ID 映射 + 引用关系,安全可靠
避免 toString()、equals()、深拷贝里的隐式递归
这些方法常被框架自动调用(如日志打印、断言、缓存 key 计算),一旦含循环字段就崩:
立即学习“Java免费学习笔记(深入)”;
- Lombok 的
@ToString加exclude = "parent"或设callSuper = true配合父类防循环 - 手写
toString()时,用ReflectionToStringBuilder.setCycleDetectionEnabled(true) - 深拷贝工具(如 BeanUtils.copyProperties)不处理循环,建议换用支持图遍历的库(如 Kryo +
setReferences(true))
业务模型层提前切断引用链
不是所有循环都需要保留。DTO 层可主动规避:
- 用
@JsonIgnore直接忽略反向字段(如 User 中的 orders 列表) - 用
@JsonManagedReference/@JsonBackReference标记主从关系,只序列化一次 - JPA 实体转 JSON 前,用 MapStruct 或手动构造扁平 DTO,剥离双向关联


















