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

Java自定义序列化中如何重写final修饰的writeObject()方法?

答疑:Java自定义序列化的writeObject方法为何不需要重写?

这问题问得特别好——Java序列化的这个设计初看确实有点反直觉,毕竟ObjectOutputStream.writeObject()确实是被final修饰的,没法直接重写,但自定义序列化的实现逻辑根本不是走“重写”这条路,而是靠JVM的反射机制+特殊约定来完成的,我给你拆解清楚:

核心逻辑:不是重写API,而是JVM主动检测私有方法

当你调用ObjectOutputStream.writeObject(obj)时,这个final方法的底层逻辑会做这么几件事:

  • 检查传入的对象是否实现了Serializable接口(没实现直接抛异常)
  • 通过反射去遍历对象所属类的方法,寻找一个签名严格匹配的私有方法:private void writeObject(ObjectOutputStream out) throws IOException
  • 如果找到这个方法,JVM会通过反射强行调用它(反射可以突破private的访问权限限制),此时序列化的逻辑就完全交给你定义的这个私有方法了
  • 如果没找到,才会执行默认的序列化流程——把对象的非transient字段逐一写入流中

对应的,反序列化时也是同样的逻辑:JVM会检测类中是否有private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException,有的话就调用它,否则走默认流程。

举个实际例子更清楚

比如我们想自定义一个User类的序列化逻辑,处理transient字段:

import java.io.*;

public class User implements Serializable {
    private String username;
    private transient int age; // 默认不会被序列化的字段

    // 自定义序列化方法,必须是private,签名严格匹配
    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject(); // 先执行默认序列化,处理非transient字段
        out.writeInt(age * 2); // 自定义处理transient的age,比如加密/修改后再写入
    }

    // 对应的反序列化方法
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject(); // 先读取默认字段
        age = in.readInt() / 2; // 还原自定义处理的age字段
    }
}

当你用ObjectOutputStream.writeObject(new User("Alice", 25))时,JVM会自动找到并调用这个私有writeObject方法,而不是走默认流程。

为什么要这么设计?

这个约定其实是为了兼顾封装性和灵活性:

  • 把自定义逻辑放在类内部的私有方法里,符合封装原则——序列化逻辑属于类本身的行为,不需要暴露给外部
  • 不需要让开发者去重写ObjectOutputStream的方法,避免了继承带来的复杂度,同时也保证了ObjectOutputStream核心API的稳定性

内容的提问来源于stack exchange,提问作者user9608350

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 03:33:18