0Pricing
Java Academy · 课时

自定义序列化:writeObject 与 readObject

重写 writeObject 和 readObject,以实现加密、压缩或自定义字段处理

自定义序列化:writeObject 与 readObject 是 CoddyKit 上的免费 Java Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Java Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Java Academy 课程共包含 4 节课。

默认序列化不够用时

默认序列化会原样保存所有非 transient 字段。有时您需要加密、压缩或使用不同的表示形式。这时就可以使用 writeObject / readObject 钩子。

声明 writeObject

添加一个 private void writeObject(ObjectOutputStream oos) 方法。Java 会调用它来代替默认机制。您可以准确控制要写入的内容。

private void writeObject(ObjectOutputStream oos) throws IOException {
    oos.defaultWriteObject(); // write non-transient fields normally
    oos.writeUTF(encrypt(this.secret)); // write encrypted secret
}

声明 readObject

添加一个 private void readObject(ObjectInputStream ois) 方法。Java 会在反序列化期间调用它。您必须按照写入时的完全相同的顺序读取数据。

private void readObject(ObjectInputStream ois)
        throws IOException, ClassNotFoundException {
    ois.defaultReadObject(); // restore non-transient fields
    this.secret = decrypt(ois.readUTF()); // decrypt and restore
}

顺序很重要

在 writeObject 中写入的内容,必须在 readObject 中按相同的顺序读回。顺序不匹配会导致 StreamCorruptedException。

private void writeObject(ObjectOutputStream oos) throws IOException {
    oos.writeInt(version);
    oos.writeUTF(data);
}
private void readObject(ObjectInputStream ois)
        throws IOException, ClassNotFoundException {
    this.version = ois.readInt();  // same order
    this.data    = ois.readUTF();
}

序列化期间压缩数据

在 writeObject 中使用 GZIPOutputStream 包装流,以压缩较大的字段;然后在 readObject 中使用 GZIPInputStream 解压缩。

private void writeObject(ObjectOutputStream oos) throws IOException {
    oos.defaultWriteObject();
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    try (GZIPOutputStream gz = new GZIPOutputStream(bos)) {
        gz.write(largePayload.getBytes());
    }
    oos.writeObject(bos.toByteArray());
}

readResolve 钩子

readResolve() 会在反序列化后调用。您可以返回一个替代对象——这对于单例和枚举很有用,可确保反序列化后只存在一个实例。

public class AppConfig implements Serializable {
    private static final AppConfig INSTANCE = new AppConfig();
    private AppConfig() {}
    public static AppConfig getInstance() { return INSTANCE; }
    private Object readResolve() { return INSTANCE; } // return singleton
}

writeReplace 钩子

writeReplace() 会在序列化前调用。您可以返回另一个对象来代替原对象进行序列化——这对于代理模式和序列化替代对象很有用。

private Object writeReplace() {
    return new UserProxy(this.id); // serialize a lightweight proxy
}

在 readObject 中验证字段

您可以在 readObject 中添加验证逻辑,作为纵深防御策略的一部分,拒绝无效或被篡改的数据。

private void readObject(ObjectInputStream ois)
        throws IOException, ClassNotFoundException {
    ois.defaultReadObject();
    if (age < 0 || age > 150) {
        throw new InvalidObjectException("Invalid age: " + age);
    }
}

处理版本更改

在 writeObject 中使用版本字段,这样 readObject 就能在迁移期间平稳地处理旧格式和新格式。

private static final int SERIAL_VERSION = 2;
private void writeObject(ObjectOutputStream oos) throws IOException {
    oos.defaultWriteObject();
    oos.writeInt(SERIAL_VERSION);
    if (SERIAL_VERSION >= 2) oos.writeUTF(email); // new field
}
private void readObject(ObjectInputStream ois)
        throws IOException, ClassNotFoundException {
    ois.defaultReadObject();
    int v = ois.readInt();
    if (v >= 2) this.email = ois.readUTF();
}

序列化代理模式

一种可靠的方法是:编写一个捕获最少状态的私有静态内部类,然后使用 writeReplace 写入代理对象,并通过代理对象的 readResolve 重建原对象。

性能考量

自定义序列化会增加方法调用开销,但可以显著减小载荷大小。优化前请先进行基准测试;如需最大程度的控制,请使用外部化机制。

快速检查

writeObject 和 readObject 中的顺序必须满足什么条件?

回顾

为了实现加密、压缩或自定义格式,请重写 writeObject / readObject。使用 readResolve 处理单例。读取顺序必须始终与写入顺序一致。

常见问题解答

「自定义序列化:writeObject 与 readObject」课时是免费的吗?

是的 — 「自定义序列化:writeObject 与 readObject」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。

「自定义序列化:writeObject 与 readObject」这节课中我会学到什么?

重写 writeObject 和 readObject,以实现加密、压缩或自定义字段处理 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Java Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「自定义序列化:writeObject 与 readObject」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Java Academy 课中编写并运行代码吗?

能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. Java 序列化基础
  2. transient 字段与 serialVersionUID
  3. 自定义序列化:writeObject 与 readObject
  4. 现代替代方案:JSON 与 Protocol Buffers
← 返回 Java Academy