自定义序列化: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 反馈 — 无需本地设置。
此课程中的所有课时
- Java 序列化基础
- transient 字段与 serialVersionUID
- 自定义序列化:writeObject 与 readObject
- 现代替代方案:JSON 与 Protocol Buffers