You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 14:24:02