ObjectOutputStream序列化对象前必须确保类实现Serializable接口并显式定义serialVersionUID,使用try-with-resources关闭流,多个对象写入需避免复用同一实例或调用reset()。

ObjectOutputStream 序列化对象前必须确保类可序列化
Java 中 ObjectOutputStream 只能序列化实现了 Serializable 接口的类,否则运行时抛出 java.io.NotSerializableException。这不是编译错误,容易漏掉检查。
- 在目标类声明后加上
implements Serializable - 强烈建议显式定义
private static final long serialVersionUID = 1L;,否则 JVM 自动生成的值依赖类结构(字段增删、重排序都会导致反序列化失败) - 被标记为
transient的字段不会被序列化,需自行处理逻辑 - 内部类(尤其是匿名或局部类)默认持有外部类引用,若外部类不可序列化,会连带失败;优先用静态内部类
写入文件时必须配合 FileOutputStream 和 try-with-resources
ObjectOutputStream 本身不负责文件 I/O,它只做序列化编码;必须套在 FileOutputStream 上。直接 new ObjectOutputStream(new FileOutputStream("a.dat")) 是常见写法,但要注意资源释放和异常嵌套。
- 务必用 try-with-resources,因为
ObjectOutputStream的close()会刷新缓冲区并写入终止标记(stream header),漏掉会导致后续读取报StreamCorruptedException - 不要在
ObjectOutputStream外层再套一层BufferedOutputStream——它不必要,且可能干扰 header 写入顺序 - 文件路径建议用
Paths.get(...).toFile()或new File(...)显式构造,避免硬编码斜杠方向问题
示例:
try (FileOutputStream fos = new FileOutputStream("user.dat");
ObjectOutputStream oos = new ObjectOutputStream(fos)) {
oos.writeObject(new User("Alice", 28));
}
多次写入同一 ObjectOutputStream 实例会引发 StreamCorruptedException
这是最常踩的坑:想循环保存多个对象,却反复调用同一个 ObjectOutputStream 的 writeObject() —— 它会在每次写入前重复写入 stream header,破坏格式。
- 如果要追加写入多个对象,有两种安全方式:
– 每次新建ObjectOutputStream(注意:header 会重复,读取端需用ObjectInputStream循环 read,忽略 header 异常)
– 更推荐:一次性收集对象到List,再整体writeObject(list) - 若坚持复用流,必须调用
oos.reset()清除已写入的 handle 缓存(用于处理对象引用),但 header 仍只写一次,适用于单个流写多个独立对象的场景 - 注意:
reset()后,之前写过的相同对象实例会被当新对象处理(不再复用引用),可能影响反序列化后的对象图结构
反序列化时 ClassLoader 和类版本必须匹配
用 ObjectInputStream 读取时,JVM 需要加载对应类,并校验 serialVersionUID。哪怕只是改了方法体、加了注释,只要 serialVersionUID 不变,就能兼容;反之,即使只动了一个字段名,没改 serialVersionUID,也会失败。
- 确保反序列化时类在 classpath 中,且与序列化时是同一份字节码(IDE 编译缓存、热部署、模块隔离都可能导致“类存在但不等价”)
- 如果类结构已变更(如删字段),可自定义
readObject()方法,在其中手动处理缺失字段的默认值 - 生产环境慎用 Java 原生序列化——它耦合紧、无跨语言能力、有安全风险(反序列化任意类可能触发恶意代码),优先考虑 JSON(Jackson/Gson)或 Protocol Buffers
实际写磁盘时,真正关键的是三件事:类标 Serializable、流必须正确关闭、多个对象别误用单一流实例。其他细节大多围绕这三点展开。











