java序列化是将运行中对象(含字段值、引用关系)转为字节流,仅保存实例状态,不保存方法、静态变量;反序列化则重建等价对象。必须实现serializable接口以通过jvm许可检查,该接口为标记接口;未实现则运行时抛notserializableexception;父类未实现时其字段丢失;内部类序列化要求外部类也实现;建议显式声明serialversionuid避免invalidclassexception;static和transient字段不参与序列化,transient字段反序列化后为默认值,且全过程绕过构造器和初始化块。

序列化就是把对象变成字节流,仅此而已
Java序列化机制的本质,是把一个运行中的对象(包括它的字段值、引用关系)转换成一串可存储或传输的字节序列。它不保存方法、静态变量、构造器,也不执行任何逻辑——只忠实记录“此刻这个对象长什么样”。反序列化则是逆向操作:从字节流中重建出结构一致、状态相同的对象实例。
为什么必须实现 Serializable 接口?
这不是语法强制,而是JVM的“许可检查”机制:ObjectOutputStream.writeOject() 在写入前会调用 obj instanceof Serializable。如果返回 false,直接抛 NotSerializableException。这个接口是纯标记接口(空接口),不定义任何方法,作用就是告诉JVM:“这个类我允许你序列化”。
- 没实现该接口 → 运行时报错,不是编译错误
- 父类未实现但子类实现了 → 子类字段能序列化,父类字段丢失(除非父类也实现或提供无参构造)
- 内部类默认持有外部类引用 → 若外部类未实现
Serializable,序列化内部类会失败
serialVersionUID 不写会怎样?
它是序列化版本号,用于校验类结构兼容性。不显式声明时,JVM 会根据类名、字段、方法等自动生成一个 64 位哈希值。一旦你改了字段名、加了 transient、删了方法……哪怕只是加了个注释,这个哈希值就可能变。结果就是反序列化时抛 InvalidClassException: local class incompatible。
- 建议始终显式声明:
private static final long serialVersionUID = 1L; - 升级类时若字段语义不变(如重命名后加
transient+ 自定义readObject),可保持 ID 不变 - IDE(如 IntelliJ)能一键生成基于当前结构的稳定 ID,比手写
1L更安全
哪些字段不会被序列化?怎么控制?
默认情况下,只有非 static、非 transient 的实例字段参与序列化。这是关键控制点:
立即学习“Java免费学习笔记(深入)”;
-
static字段属于类,不属于对象实例 → 跳过 -
transient字段明确标记“不持久化” → 跳过(如密码、临时缓存) - 若需更精细控制(如加密敏感字段、跳过某些引用),可自定义
writeObject和readObject方法 - 注意:
transient字段在反序列化后为默认值(int是 0,Object是null),不会调用构造器初始化
transient 或是否在声明时已赋值。这些细节在深拷贝或安全敏感场景下,会直接导致行为偏差。






