Java中序列化是怎么实现的?

在 Java 中,序列化(Serialization) 是将对象转换为字节流的过程,以便存储到文件、数据库或通过网络传输;反序列化(Deserialization) 则是将字节流还原为对象的过程。Java 通过 java.io.Serializable 接口和相关 API 实现这一机制。以下是其核心原理和实现细节:


1. 实现序列化的条件

  • 实现 Serializable 接口
    类必须显式声明实现 Serializable 接口(标记接口,无方法)。

    public class User implements Serializable {
        private String name;
        private int age;
        // 必须有无参构造方法(反序列化时反射调用)
        public User() {}
        // getter/setter
    }
  • serialVersionUID
    可选字段,用于标识类的版本。若未显式定义,JVM 会根据类结构自动生成,但类结构变化时可能导致反序列化失败。

    private static final long serialVersionUID = 1L; // 显式定义

2. 序列化与反序列化流程

(1) 序列化(Object → 字节流)
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.dat"))) {
    User user = new User("Alice", 30);
    oos.writeObject(user); // 序列化对象
}
  • 底层机制

    1. 递归写入对象及其所有引用的非 transient、非 static 字段。

    2. 每个对象分配唯一 ID,避免重复写入相同对象(通过引用共享)。

(2) 反序列化(字节流 → Object)
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.dat"))) {
    User user = (User) ois.readObject(); // 反序列化
}
  • 底层机制

    1. 根据字节流重建对象,不调用构造方法(直接分配内存,通过反射设置字段值)。

    2. 检查 serialVersionUID:若与当前类版本不匹配,抛出 InvalidClassException


3. 控制序列化行为

(1) transient 关键字

标记字段不参与序列化:

private transient String password; // 不会被序列化
(2) 自定义序列化逻辑

通过重写 writeObject() 和 readObject() 方法:

private void writeObject(ObjectOutputStream oos) throws IOException {
    oos.defaultWriteObject(); // 默认序列化
    oos.writeUTF(encrypt(password)); // 自定义加密
}

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
    ois.defaultReadObject(); // 默认反序列化
    this.password = decrypt(ois.readUTF()); // 自定义解密
}
(3) Externalizable 接口

完全控制序列化过程(需实现 writeExternal() 和 readExternal()):

public class User implements Externalizable {
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeUTF(name);
        out.writeInt(age);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException {
        this.name = in.readUTF();
        this.age = in.readInt();
    }
}

4. 序列化的限制与风险

  • 不可序列化的字段

    • static 字段:属于类,不参与序列化。

    • transient 字段:显式排除。

  • 安全风险

    • 反序列化可能执行恶意代码(如通过重写 readObject())。

    • 解决方案:避免反序列化不可信数据,或使用白名单验证(如 Apache Commons IO 的 ValidatingObjectInputStream)。


5. 替代序列化方案

方案特点
JSON/XML跨语言、可读性强(如 Jackson、Gson)
Protocol Buffers高效二进制格式,支持跨语言(Google 出品)
Kryo高性能 Java 序列化库(常用于分布式缓存、RPC)
Java 原生序列化简单但性能低、安全性差,仅适用于 Java 环境

6. 关键注意事项

  • 版本兼容性:修改类结构时需谨慎处理 serialVersionUID

  • 性能优化:避免序列化大对象或深层嵌套结构。

  • 敏感数据保护:使用 transient 或自定义加密逻辑。


总结

Java 序列化通过 Serializable 接口和 ObjectInputStream/ObjectOutputStream 实现,适用于简单场景,但存在性能和安全隐患。实际开发中,推荐优先选择 JSON、Protobuf 等跨语言、高性能的序列化方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

looken1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值