| 问题 | 核心回答 |
|---|---|
| 定义 | 对象 ←> 字节流 (用于传输或存储) |
| 接口 | 必须实现 Serializable |
| 版本号 | serialVersionUID 用于验证版本一致性,建议手动固定 |
| 排除字段 | 使用 transient 关键字 |
| 构造函数 | 反序列化时不执行构造函数 (直接从二进制流重构内存) |
1. 什么是序列化与反序列化?
-
序列化 (Serialization): 将 Java 对象转换成 二进制字节流 的过程。
- 用途: 可以把对象保存到文件(持久化)、数据库,或者在网络上传输(比如 RPC 调用)。
-
反序列化 (Deserialization): 将字节流恢复成 Java 对象的过程。
2. 怎么实现序列化?
-
核心接口: 类必须实现
java.io.Serializable接口。- 注意: 这是一个 标记接口 (Marker Interface),里面没有任何方法,只是给 JVM 贴个条子,说“这个对象允许被序列化”。
-
代码实现:
-
序列化: 使用
ObjectOutputStream的writeObject(obj)方法。 -
反序列化: 使用
ObjectInputStream的readObject()方法。
-
3. 面试必考:serialVersionUID 有什么用?
-
定义: 它是序列化版本的 身份 ID。
private static final long serialVersionUID = 1L;
-
作用: 验证序列化前后的类是否兼容。
-
场景说明:
-
你把一个
User对象序列化存到了文件里(当时 ID=1)。 -
后来你修改了
User类(比如加了个字段),但没指定 ID,JVM 会自动重新计算一个新 ID(比如 ID=2)。 -
当你尝试从文件反序列化时,JVM 发现 ID 不一致(1 != 2),就会抛出
InvalidClassException异常。
-
-
最佳实践: 一定要手动指定一个固定的
serialVersionUID。这样即使你以后给类加了新字段,旧的序列化数据依然能读出来(新字段给默认值),保证了版本兼容性。
4. 怎么不序列化某个字段?
-
使用
transient关键字修饰。 -
例子:
private transient String password; -
结果: 序列化时,这个字段会被跳过。反序列化回来时,这个字段会变成默认值(如 null)。
5.怎么把一个对象从一个 JVM 转移到另一个 JVM?
“这个过程主要是通过序列化(Serialization)和反序列化机制来实现的。因为不同 JVM 间的内存是隔离的,无法直接传递引用。 (加分项 - 补充实际场景): 不过在实际的分布式或微服务架构(如 RPC 调用)中,我们很少直接用 Java 原生的序列化,因为它性能较差且生成的字节流太大。我们通常会使用 JSON(如 Jackson)、Protobuf 或者 Hessian 等更高效、跨语言的序列化协议来完成对象的传输。”