问题核心回答
定义对象 > 字节流 (用于传输或存储)
接口必须实现 Serializable
版本号serialVersionUID 用于验证版本一致性,建议手动固定
排除字段使用 transient 关键字
构造函数反序列化时不执行构造函数 (直接从二进制流重构内存)

1. 什么是序列化与反序列化?

  • 序列化 (Serialization): 将 Java 对象转换成 二进制字节流 的过程。

    • 用途: 可以把对象保存到文件(持久化)、数据库,或者在网络上传输(比如 RPC 调用)。
  • 反序列化 (Deserialization): 将字节流恢复成 Java 对象的过程。

2. 怎么实现序列化?

  • 核心接口: 类必须实现 java.io.Serializable 接口。

    • 注意: 这是一个 标记接口 (Marker Interface),里面没有任何方法,只是给 JVM 贴个条子,说“这个对象允许被序列化”。
  • 代码实现:

    • 序列化: 使用 ObjectOutputStreamwriteObject(obj) 方法。

    • 反序列化: 使用 ObjectInputStreamreadObject() 方法。

3. 面试必考:serialVersionUID 有什么用?

  • 定义: 它是序列化版本的 身份 ID

    • private static final long serialVersionUID = 1L;
  • 作用: 验证序列化前后的类是否兼容。

  • 场景说明:

    1. 你把一个 User 对象序列化存到了文件里(当时 ID=1)。

    2. 后来你修改了 User 类(比如加了个字段),但没指定 ID,JVM 会自动重新计算一个新 ID(比如 ID=2)。

    3. 当你尝试从文件反序列化时,JVM 发现 ID 不一致(1 != 2),就会抛出 InvalidClassException 异常。

  • 最佳实践: 一定要手动指定一个固定的 serialVersionUID。这样即使你以后给类加了新字段,旧的序列化数据依然能读出来(新字段给默认值),保证了版本兼容性

4. 怎么不序列化某个字段?

  • 使用 transient 关键字修饰。

  • 例子: private transient String password;

  • 结果: 序列化时,这个字段会被跳过。反序列化回来时,这个字段会变成默认值(如 null)。

5.怎么把一个对象从一个 JVM 转移到另一个 JVM?

“这个过程主要是通过序列化(Serialization)反序列化机制来实现的。因为不同 JVM 间的内存是隔离的,无法直接传递引用。 (加分项 - 补充实际场景): 不过在实际的分布式或微服务架构(如 RPC 调用)中,我们很少直接用 Java 原生的序列化,因为它性能较差且生成的字节流太大。我们通常会使用 JSON(如 Jackson)、Protobuf 或者 Hessian 等更高效、跨语言的序列化协议来完成对象的传输。”