java对象序列化需实现serializable接口,由jvm内置机制配合objectoutputstream/objectinputstream完成;未实现则抛notserializableexception;static和transient字段不参与序列化;serialversionuid保障版本兼容;可自定义writeobject/readobject方法控制过程。

Java 对象序列化靠 Serializable 接口实现,它不提供任何方法,只是一种“声明”——告诉 JVM:“这个类的对象可以被转成字节流”。真正起作用的是 JVM 内置的序列化机制,配合 ObjectOutputStream 和 ObjectInputStream 完成存储与传输。
为什么必须实现 Serializable?
没实现这个接口的类,调用 writeObject() 会直接抛 NotSerializableException。JVM 通过反射检查类是否标记为可序列化,这是硬性门槛。即使所有字段都是基本类型或 String,只要没实现该接口,就无法走默认序列化流程。
- 父类未实现 Serializable,子类仍可实现,但反序列化时父类字段会被其无参构造器重置(要求父类有可访问的无参构造)
- 接口、抽象类本身不能被序列化,只有具体实现类可以
- 数组类型如果元素类型可序列化,则整个数组可序列化
哪些字段不会被序列化?
默认情况下,只有非静态、非 transient 的实例字段参与序列化。其他一律跳过:
-
static字段属于类,不属于对象实例,自然不保存 -
transient字段显式声明“不参与序列化”,比如密码、临时缓存、数据库连接等敏感或不可序列化资源 - 方法、内部类定义、枚举常量(枚举本身可序列化,但它的实例是单例且由 JVM 管理)
serialVersionUID:版本兼容性的关键
反序列化时,JVM 会比对字节流中的 serialVersionUID 和当前类的值。不一致就报 InvalidClassException。JVM 自动生成的值依赖于类名、字段名/类型/修饰符、方法签名等,哪怕加个空格都可能变化。
- 强烈建议显式声明:
private static final long serialVersionUID = 1L; - 升级类结构时(如新增字段),保持 UID 不变可向后兼容;若语义已变,应主动修改 UID
- IDE(如 IntelliJ)通常能一键生成基于当前结构的稳定 UID 值
自定义序列化逻辑怎么写?
当默认行为不满足需求时(比如加密敏感字段、跳过某些计算字段、兼容旧版本格式),可通过私有方法介入:
-
private void writeObject(ObjectOutputStream out) throws IOException:先调out.defaultWriteObject()执行默认逻辑,再手动写额外数据 -
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException:先调in.defaultReadObject()恢复默认字段,再手动读取并还原 -
writeReplace()和readResolve()用于替换序列化/反序列化对象,常用于单例模式保全唯一性
不复杂但容易忽略:序列化不是万能方案,它耦合类结构、依赖 JVM 实现、存在安全风险(反序列化漏洞)。生产环境涉及网络传输时,更推荐 JSON、Protobuf 等跨语言格式;仅在 Java 生态内闭环使用(如 RMI、本地缓存、配置快照)才优先考虑 Serializable。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











