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
相关产品推荐
相关产品推荐

