一、序列化:对象到字节的魔幻转变
核心目标:将内存中的对象状态转换为可存储/传输的字节序列(反之即反序列化)。
实现基础:实现空接口 java.io.Serializable(标记接口,无方法定义)。
import java.io.*;
// 1. 定义可序列化对象
class Person implements Serializable {
private static final long serialVersionUID = 1L; // 显式声明版本号
private String name;
private transient int age; // transient字段不被序列化
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
public class SerializationDemo {
public static void main(String[] args) {
Person person = new Person("Alice", 30);
// 2. 序列化对象到文件
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.dat"))) {
oos.writeObject(person);
System.out.println("对象已序列化");
} catch (IOException e) {
e.printStackTrace();
}
// 3. 从文件反序列化对象
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.dat"))) {
Person restoredPerson = (Person) ois.readObject();
System.out.println("反序列化对象: " + restoredPerson); // 输出: Person{name='Alice', age=0} (age因transient为默认值0)
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
二、关键机制深度剖析
- serialVersionUID 的作用
-
- 唯一标识序列化类的版本,反序列化时JVM会校验该ID是否一致
- 未显式声明时:编译器根据类结构自动生成,类细节变更将导致ID变化 → 引发
InvalidClassException - 最佳实践:始终显式声明
private static final long serialVersionUID
- transient 关键字的威力
-
- 标记字段不参与序列化过程
- 适用于敏感数据(如密码)或依赖运行时环境的字段(如线程句柄)
序列化过程揭秘
- 图表代码
三、进阶实践与安全警示
自定义序列化逻辑
通过重写 writeObject() 和 readObject() 方法,可完全控制序列化行为:
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject(); // 默认序列化
oos.writeInt(age * 2); // 自定义加密操作
}
- 警惕反序列化漏洞
反序列化会执行对象的构造逻辑,攻击者可构造恶意字节流触发危险操作 → 解决方案:
-
- 使用
ObjectInputFilter验证输入流 - 避免反序列化不可信数据源
- 使用
四、序列化替代方案
- JSON/XML:跨语言、可读性高(如Jackson/Gson)
- Protocol Buffers/Thrift:高性能二进制协议,强类型约束
- Java外部化(Externalizable):完全自定义序列化过程
核心结论:Java原生序列化虽便捷,但需警惕版本兼容与安全隐患。在新系统中优先考虑JSON或高效二进制方案,遗留系统务必严格管理serialVersionUID和transient字段。
通过穿透字节流的迷雾,开发者得以驾驭对象持久化与通信的底层力量——这正是Java序列化赋予我们的魔幻钥匙,亦是构建健壮分布式系统的基石。

被折叠的 条评论
为什么被折叠?



