Java序列化是将对象转为字节流以存储或传输,需显式实现Serializable接口作为JVM安全检查的硬性门槛;transient和static字段不参与序列化,反序列化不调用构造函数;因体积大、性能差、语言绑定强及安全风险,生产环境推荐Jackson等替代方案。

Java序列化就是把内存里的对象“拍扁”成一串字节,以便存文件、走网络或跨JVM传递;Serializable接口本身不干任何事,它只是个标签——告诉JVM“这个类允许被自动序列化”。
为什么必须实现 Serializable 接口?
不实现它,调用 ObjectOutputStream.writeObject() 会直接抛 NotSerializableException。JVM靠这个接口做安全检查,不是语法糖,是硬性准入门槛。
- 接口无方法,纯标记作用,但必须显式声明:
class Person implements Serializable - 若父类没实现,子类实现也没用——父类字段不会被序列化(除非父类也实现了或字段是
transient) - 推荐显式声明
private static final long serialVersionUID,否则JVM自动生成,类结构稍有变动(如加个字段)就可能反序列化失败
ObjectOutputStream 和 ObjectInputStream 怎么用?
这是序列化/反序列化的“搬运工”,底层依赖 FileOutputStream/FileInputStream 等流,但你只管对象,不用操心字节怎么拼。
- 写入时:用
FileOutputStream包一层,再套ObjectOutputStream,然后调writeObject(obj) - 读取时:顺序必须严格对应,先开
FileInputStream,再套ObjectInputStream,再调readObject()并强制转型 - 注意捕获两个异常:
IOException(流问题)和ClassNotFoundException(反序列化时找不到类定义)
try (FileOutputStream fos = new FileOutputStream("user.ser");
ObjectOutputStream oos = new ObjectOutputStream(fos)) {
oos.writeObject(new User("Alice", 28));
} catch (IOException e) {
e.printStackTrace();
}
哪些字段不会被序列化?常见陷阱有哪些?
默认只序列化非 static、非 transient 的实例字段。但真正踩坑的往往不是规则,而是隐含行为。
立即学习“Java免费学习笔记(深入)”;
-
transient字段跳过序列化,但反序列化后为默认值(null、0、false),不会调构造函数重置 - 如果类里有不可序列化的成员(比如
Thread、Socket),又没标transient,运行时才报错 - 集合类如
ArrayList可序列化,但里面存了自定义对象,那些对象也得实现Serializable,否则链路断在中间 - 反序列化不走构造函数(包括无参构造),所以初始化逻辑(如字段赋默认值、资源预加载)不会执行
什么时候该换别的序列化方式?
原生 Java 序列化适合开发调试、RMI、老系统内部通信,但生产环境要谨慎:
- 字节体积大、性能慢,比 JSON 或 Protobuf 大 3–5 倍,序列化/反序列化耗时高
- 完全 Java 生态绑定,无法被 Go/Python 等语言解析
- 存在严重反序列化漏洞风险(如
InvokerTransformer链),除非你完全信任数据来源,否则别用在 HTTP 请求体或 MQ 消息中 - 类结构变更兼容性差——删字段、改类型、改访问修饰符都可能让旧数据无法读取
真正需要跨服务、高性能或安全可控时,Serializable 就该让位给 Jackson(JSON)、Kryo 或 Protobuf。








