什么是序列化?如何序列化?为什么要序列化?反序列化会遇到哪些问题?¶
一、什么是序列化¶
把对象转成字节流,以便: - 保存到磁盘。 - 网络传输。
反序列化就是把字节流恢复成对象。
二、Java 序列化¶
1. 实现 Serializable 接口¶
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private transient int age; // transient 不序列化
}
2. 为什么要 serialVersionUID¶
显式声明版本号。反序列化时如果版本不一致,抛 InvalidClassException。
不声明的话,JVM 根据类结构自动生成,类一改就变。
3. transient¶
被 transient 修饰的字段不序列化。反序列化后是默认值(0/null)。
4. 序列化方式¶
// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("u.bin"));
oos.writeObject(user);
// 反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("u.bin"));
User u = (User) ois.readObject();
三、为什么要序列化¶
- 网络传输:RPC、RMI。
- 持久化:对象存磁盘、缓存。
- 深拷贝:序列化再反序列化。
四、反序列化的问题¶
1. 版本不一致¶
类改了字段,旧字节流反序列化失败。
2. 安全漏洞¶
反序列化不可信数据可能执行恶意代码(Fastjson、Jackson 历史漏洞)。
- 不要反序列化不可信数据。
- 用白名单。
3. transient 字段丢失¶
反序列化后 transient 字段是默认值。
4. 静态字段不序列化¶
static 属于类,不属于对象。
5. 继承关系¶
父类实现 Serializable,子类自动实现;子类实现,父类不实现,父类字段用默认构造初始化。
6. 性能¶
Java 原生序列化慢,体积大。常用: - JSON - Protobuf - Hessian - Kryo
五、最佳实践¶
- 显式声明 serialVersionUID。
- 敏感字段用 transient。
- 不要用原生序列化做跨语言。
- 用 JSON/Protobuf。
高频追问
- 为什么序列化不保存 static?static 是类级别的。
- 为什么要实现 Serializable 接口?只是标记,JVM 看到才允许序列化。