Externalizable接口实现完全自定义序列化,需显式实现接口、提供public无参构造器,并重写writeExternal与readExternal方法,字段读写完全由开发者控制,支持精细版本兼容。

通过 Externalizable 接口实现完全自定义的序列化与反序列化,核心在于**放弃默认机制、接管全部读写逻辑**。它不是“增强 Serializable”,而是彻底重写对象如何被写入字节流、又如何从字节流重建——包括字段选择、顺序、格式甚至跨版本兼容策略。
必须满足的三个前提条件
缺一不可,否则运行时会抛出 java.io.InvalidClassException 或 java.lang.InstantiationException:
- 类必须显式实现
Externalizable接口(而非仅Serializable) - 必须提供一个 public 的无参构造函数 —— 反序列化时 JVM 会用它先创建空对象,再调用
readExternal() - 必须重写
writeExternal(ObjectOutput out)和readExternal(ObjectInput in)两个方法
字段控制权完全交由你决定
Externalizable 不会自动处理任何字段,哪怕它们是 public 或非 transient。你要自己决定:哪些字段写、按什么顺序写、用什么方式写。例如:
若只希望保存 name 和 number,而忽略其他字段或计算属性,就在 writeExternal 中只调用 out.writeXXX() 对应两次:
立即学习“Java免费学习笔记(深入)”;
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(name); // 显式写字符串
out.writeInt(number); // 显式写整数
// 其他字段如 timestamp、computedValue 等,不写就不参与序列化
}
对应地,readExternal 必须以完全相同的顺序和类型读取:
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = in.readUTF(); // 顺序、类型必须严格匹配 writeExternal
number = in.readInt();
}
支持精细的版本兼容与数据迁移
因为读写逻辑全由你掌控,你可以轻松应对字段增删、类型变更等场景。例如:
- 新增字段:在
writeExternal末尾追加写入,在readExternal末尾尝试读取;若旧版本流中没有该字段,捕获EOFException并设默认值 - 删除字段:直接从两个方法中移除对应读写语句,旧数据仍可成功反序列化(只要不破坏已有字段的读取顺序)
- 字段重命名或拆分:在
readExternal中读取旧字段后,赋值给新字段名或做转换计算
这种灵活性是 Serializable 配合 serialVersionUID 无法提供的——后者依赖 JVM 默认机制,字段变动极易引发 InvalidClassException。
注意与 Serializable 的本质区别
别混淆两者定位:
-
Serializable是“声明式”:加接口即启用默认行为,用transient或serialPersistentFields做有限排除 -
Externalizable是“命令式”:接口只是标记,所有序列化逻辑必须手动编码,连父类字段也不会自动处理——除非你在方法里显式调用super.writeExternal()/readExternal()
因此,它适合对序列化体积、性能或跨语言/跨版本有强要求的场景,比如高频 RPC 参数、长期存档数据,但开发成本更高,需严格保证读写逻辑对称。


















