Java序列化是将对象状态转为字节流以实现持久化或传输的机制,需实现Serializable接口并配合ObjectOutputStream/ObjectInputStream使用;transient和static字段不参与,建议显式声明serialVersionUID以避免版本不兼容;它保存对象状态而非行为,不适合跨语言或长期存储。

Java里的序列化,就是把一个活在内存里的对象“打包成字节流”,让它能存进文件、发到网络另一头,或者等JVM重启后再原样还原回来。它不是魔法,而是一套有明确规则的持久化机制——核心就两条:Serializable 接口标记 + ObjectOutputStream/ObjectInputStream 执行。
为什么必须实现 Serializable 接口?
这不是可选项,而是JVM的硬性准入门槛。没实现这个接口,调用 writeObject() 会立刻抛出 NotSerializableException。它是个纯标记接口(没有方法),作用只有一个:告诉JVM“这个类我允许你深挖它的字段、递归处理引用、生成元数据”。
- 父类没实现
Serializable,子类实现了也没用——父类字段不会被序列化(除非父类也实现) -
transient修饰的字段会被跳过,反序列化后是默认值(null、0、false) -
static字段天生不参与序列化——它属于类,不属于某个具体对象
serialVersionUID 到底要不要写?
要,而且强烈建议显式声明。JVM在没写时会根据类名、字段、方法签名等自动生成一个哈希值;但只要类结构稍有改动(比如加个字段、改个访问修饰符),这个哈希值就变,导致反序列化时抛出 InvalidClassException——哪怕你只是改了个注释。
public class User implements Serializable {
private static final long serialVersionUID = 1L; // 显式声明,稳定可控
private String name;
private int age;
}
- 值用
1L就够用,除非你真需要做版本兼容控制(比如旧数据必须能被新类读取) - IDE(如IntelliJ)通常能自动提示并生成该字段,别手抖删掉
- 如果类只用于临时传输(比如内部RPC且服务端/客户端同步升级),可暂时省略,但上线前务必补上
序列化到底保存了什么?
它保存的是对象的**状态**,不是行为。具体包括:
立即学习“Java免费学习笔记(深入)”;
- 所有非
transient、非static的字段值(含基本类型和对象引用) - 完整的类元数据:全限定类名、
serialVersionUID、字段名与类型、继承链信息 - 对象图(Object Graph):若字段引用了其他对象,这些对象也会被递归序列化(前提是它们也都可序列化)
注意:构造方法、普通方法、final 字段(只要不是 transient)都会被保留值;但不会执行任何构造逻辑——反序列化创建对象时,Serializable 类绕过所有构造器,直接分配内存并填充字段。
最容易被忽略的一点是:序列化不是万能的持久化方案。它耦合JVM版本、类结构、甚至部分JDK实现细节;跨语言、跨平台、长期存储都极不推荐。真正需要持久化的场景(比如用户资料),应该用JSON、Protobuf或数据库——序列化更适合短生命周期的进程内缓存、RMI参数、Session落盘这类“自己人之间快速传一下”的场合。










