Java中如何序列化/反序列化包含自身类列表成员的类?
Java包含自引用列表类的序列化与反序列化方案
可行性结论
你提到的场景完全可以正常序列化,不用担心无限递归问题。Java原生序列化机制默认内置了重复引用/循环引用的处理逻辑,不会出现栈溢出的情况。
核心原理
Java的ObjectOutputStream执行序列化时,会为每个首次出现的对象分配唯一的标识ID,后续再遇到指向同一对象的引用时,只会写入该ID而非重复序列化整个对象内容,因此哪怕类内部存在多层自引用、甚至对象之间互相引用,都不会触发无限递归。
完整示例代码
首先补全可正常运行的Target类:
import java.io.Serializable; import java.util.ArrayList; import java.util.List; public class Target implements Serializable { // 显式声明序列化版本号,避免类结构变更后反序列化失败 private static final long serialVersionUID = 1L; // 保证final修饰的name有初始值,这里通过构造方法赋值 private final String name; private List<Target> dependsOnList = new ArrayList<>(); private List<Target> requiredFor = new ArrayList<>(); public Target(String name) { this.name = name; } // 省略getter、setter方法 public String getName() { return name; } public List<Target> getDependsOnList() { return dependsOnList; } public List<Target> getRequiredFor() { return requiredFor; } }
序列化与反序列化测试代码:
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class SerializeTest { public static void main(String[] args) { // 构造存在循环引用的测试对象 Target targetA = new Target("A"); Target targetB = new Target("B"); targetA.getDependsOnList().add(targetB); targetB.getRequiredFor().add(targetA); // 序列化 try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("target.obj"))) { oos.writeObject(targetA); } catch (Exception e) { e.printStackTrace(); } // 反序列化 try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("target.obj"))) { Target deserializedA = (Target) ois.readObject(); // 验证引用关系正常 System.out.println(deserializedA.getDependsOnList().get(0).getName()); // 输出B System.out.println(deserializedA.getDependsOnList().get(0).getRequiredFor().get(0).getName()); // 输出A } catch (Exception e) { e.printStackTrace(); } } }
注意事项
- 所有参与序列化的成员变量对应的类型都需要实现
Serializable接口,示例中的ArrayList本身已经实现该接口,无需额外处理 - 不需要序列化的字段可以用
transient关键字修饰,反序列化后这类字段会被赋值为对应类型的默认值(比如引用类型为null,int为0等) - 如果使用第三方序列化框架(如Jackson、Kryo等),需要开启框架的循环引用处理配置,例如Jackson可通过给类添加
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "name")注解解决循环引用序列化问题 - 生产环境建议始终显式声明
serialVersionUID,避免JVM自动生成的版本号在类结构小幅修改后不兼容,导致反序列化失败
内容的提问来源于stack exchange,提问作者david davidson
相关产品推荐
相关产品推荐

