Java序列化与反序列化:原理、实践与陷阱
一、什么是序列化与反序列化?
序列化(Serialization) 是将对象转换为字节流的过程,使得对象的状态可以被保存到文件、数据库或通过网络传输。
序列化应用场景:
- 对象持久化:可以将对象的字节序列永久地保存到硬盘上,通常存放在一个文件中,以便长期保存。
- 网络传输:将对象转换为有序字节流,以便在网络上传输。
- 性能优化:通过选择合适的序列化格式,可以提高系统的性能。
反序列化(Deserialization) 则是将字节流恢复为原始对象的过程。这两个机制是Java实现数据持久化和远程通信的核心技术。
序列化与反序列化的区别
- 操作方向:序列化是将对象转换为字节序列,而反序列化是将字节序列转换回对象。
- 目的:序列化主要用于数据的存储和网络传输,而反序列化用于从存储或传输的数据中恢复对象。
- 应用场景:序列化通常用于对象持久化和网络传输,而反序列化用于数据恢复和对象重建。
反序列化应用场景:
- 对象恢复:从硬盘或网络中读取字节序列,并将其恢复为对象。
- 数据传输和交换:在不同系统或组件之间传输和交换数据。
二、为什么需要序列化?
-
网络传输:在分布式系统中传输对象
-
持久化存储:将对象状态保存到文件或数据库
-
跨JVM通信:不同Java虚拟机间的对象交换
-
缓存恢复:快速重建复杂对象状态
三、如何实现序列化?
1. 基础实现
// 实现Serializable标记接口
public class User implements Serializable {
private static final long serialVersionUID = 1L; // 版本标识
private String username;
private transient String password; // 不被序列化
// 构造方法、getter/setter省略
}
// 序列化
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("user.dat"))) {
oos.writeObject(user);
}
// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("user.dat"))) {
User restoredUser = (User) ois.readObject();
}
2. 关键特性
-
transient关键字:阻止字段序列化
-
serialVersionUID:验证版本一致性
-
自定义序列化:通过实现
writeObject()
和readObject()
方法
四、序列化注意事项
1. 安全风险
-
反序列化漏洞:攻击者可能构造恶意字节流
// 反例:反序列化不可信数据
ObjectInputStream ois = new ObjectInputStream(untrustedInputStream);
ois.readObject(); // 高风险操作!
防护建议:
-
使用白名单验证
-
替换原生反序列化方案(如使用JSON)
-
升级安全补丁
五、常见问题
Q:静态变量会被序列化吗?
A:不会,静态变量属于类级别,不被序列化
Q:父类字段如何序列化?
A:父类需实现Serializable接口,否则需要无参构造方法
Q:为什么推荐自定义serialVersionUID?
A:自动生成的UID对类修改敏感,可能导致意外InvalidClassException