Java 对象序列化:持久化、反序列化与版本演进
对象序列化将对象转为字节流(含类元数据、字段值),可持久化到文件/数据库/网络传输;反序列化重建对象。跨 JVM、跨平台兼容。
核心接口与流
Serializable:标记接口,无方法,声明类可序列化ObjectOutputStream:writeObject(Object)序列化ObjectInputStream:readObject()反序列化
序列化条件
- 类实现
Serializable - 所有非 transient 字段均可序列化(基本类型、String、实现 Serializable 的对象)
Employee 示例
public class Employee implements Serializable {
private static final long serialVersionUID = 1L; // 显式声明版本ID
public String name;
public String address;
public transient int SSN; // transient:不序列化
public int number;
public void mailCheck() { System.out.println("邮寄给 " + name); }
}
序列化与反序列化实战
序列化
Employee e = new Employee();
e.name = "Reyan Ali"; e.address = "Phokka Kuan"; e.SSN = 11122333; e.number = 101;
try (FileOutputStream fos = new FileOutputStream("employee.ser");
ObjectOutputStream oos = new ObjectOutputStream(fos)) {
oos.writeObject(e);
System.out.println("序列化完成");
}
反序列化
Employee e = null;
try (FileInputStream fis = new FileInputStream("employee.ser");
ObjectInputStream ois = new ObjectInputStream(fis)) {
e = (Employee) ois.readObject(); // 需强转
} catch (ClassNotFoundException ex) {
System.err.println("类未找到,版本不匹配?");
}
System.out.println("姓名: " + e.name); // Reyan Ali
System.out.println("SSN: " + e.SSN); // 0 (transient 字段丢失,默认值)
关键细节
- serialVersionUID:显式声明避免编译器生成不稳定 ID,版本兼容核心
- transient:敏感数据(密码)、派生字段、非序列化资源(Socket、Thread)
- static 字段:属于类不属于对象,**不序列化**
- 构造器不调用**:反序列化直接分配内存填充字段,无参构造器不执行
- 继承**:父类实现 Serializable 则子类自动可序列化;父类未实现则反序列化时调用父类无参构造器初始化父字段
版本演进兼容性
| 变更 | 兼容性 | 处理 |
|---|---|---|
| 增加字段 | ✅ 兼容 | 新字段取默认值 |
| 删除字段 | ✅ 兼容 | 忽略流中数据 |
| 字段类型变更 | ❌ 不兼容 | 抛 InvalidClassException |
| 修改 serialVersionUID | ❌ 不兼容 | 强制不兼容 |
| 实现/移除 Serializable | ❌ 不兼容 | 流结构根本改变 |
自定义序列化逻辑
实现两个私有方法(特定签名),完全控制过程:
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject(); // 默认序列化非 transient 字段
out.writeInt(computedHash); // 额外写入派生值
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject(); // 默认读取
this.computedHash = computeHash(); // 重新计算
}
替代方案
- JSON:Jackson/Gson,人类可读、跨语言、版本容忍度高
- Protobuf/Avro:二进制、Schema 驱动、高性能、强契约
- Externalizable:完全手动控制,实现
writeExternal/readExternal
安全警示
反序列化不可信数据可导致远程代码执行 (RCE)(Gadget Chain)。防御:
- 避免反序列化不可信源数据
- 使用
ObjectInputFilter(Java 9+) 白名单类 - 改用 JSON/Protobuf 等安全格式
- 设置
-Djdk.serialFilter=...全局过滤
java
- 深入理解 Java final 关键字:变量、方法与类的不可变性
- Java instanceof 运算符完全指南:用法、继承与接口检查
- 精通 Java 继承:核心概念、类型与实战示例
- 深入理解 Java 静态嵌套类:用法、差异与实战示例
- 精通 Java 匿名内部类:定义、语法与实战示例
- Java 单例模式详解:安全实现与使用指南
- 精通 Java 反射:运行时检查类、方法与字段
- 精通 Java ArrayList:操作、方法与最佳实践
- 掌握 Java ObjectOutputStream:序列化、核心方法与实战示例
- 掌握 Java 泛型:构建可复用、类型安全的代码
- 掌握 Java 文件操作:java.io 包下的创建、读取、写入与删除
- 精通 Java:理解对象、类与核心 OOP 概念