Java 序列化
序列化(Serialization)是把 Java 对象转换为字节序列并写入文件、数据库或网络的过程,反序列化则是把字节序列还原为对象。有了序列化,对象才能落盘、跨进程传输,它是 RPC、缓存、消息队列、Session 持久化等技术的底层基石,Java 后端开发者必须掌握。
序列化的概念与用途
一句话概括:序列化 = 对象 → 字节流;反序列化 = 字节流 → 对象。典型用途:
- 对象存入磁盘,程序重启后仍能恢复;
- 对象在网络上传输(Socket、HTTP、RPC);
- 对象存入 Redis、消息队列等中间件;
- 借助"先序列化再反序列化"实现深拷贝。
实现 Serializable 接口
一个类只需实现标记接口 java.io.Serializable 即可被序列化,该接口本身没有任何方法:
import java.io.Serializable;
public class User implements Serializable {
private String name;
private int age;
// 构造方法、getter/setter、toString 省略
}
未实现该接口就写入对象流,会抛出 java.io.NotSerializableException。
serialVersionUID 的作用
它是类的"版本号",反序列化时 JVM 用它校验字节流与当前类是否兼容:
public class User implements Serializable {
private static final long serialVersionUID = 1L; // 显式声明,推荐
private String name;
private int age;
}
若不声明,JVM 会按类结构自动生成;一旦类增删字段导致编号变化,旧数据反序列化将抛 InvalidClassException。显式固定编号后,字段小幅升级也能兼容旧数据。
序列化到文件完整流程
把 User 写成非 public 类与主类同放一个文件,即可直接运行完整程序:
import java.io.*;
class User implements Serializable {
private static final long serialVersionUID = 1L;
String name;
int age;
User(String n, int a) { name = n; age = a; }
public String toString() { return "User{name=" + name + ", age=" + age + "}"; }
}
public class SerializeDemo {
public static void main(String[] args) throws Exception {
User u = new User("张三", 25);
// 第一步:写对象到文件
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("user.dat"))) {
oos.writeObject(u);
}
// 第二步:从文件读回对象
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("user.dat"))) {
User back = (User) ois.readObject();
System.out.println(back); // 输出:User{name=张三, age=25}
}
}
}
writeObject 负责序列化、readObject 负责反序列化;两者都实现 AutoCloseable,配合 try-with-resources 自动关闭文件句柄。
transient 字段
不想落盘的字段用 transient 修饰,序列化时被跳过,反序列化时取默认值:
public class User implements Serializable {
private String name;
private transient String password; // 密码不参与序列化
private transient int age; // 反序列化后 age 为 0
}
反序列化安全提示
- 反序列化会执行 readObject,恶意构造的数据可能触发远程代码执行,切勿反序列化不可信来源的数据。
- 密码、令牌等敏感信息加 transient 或加密后再存,防止明文泄漏。
- 线上类结构变更前先确认 serialVersionUID 与旧数据一致,再发布升级。
小结:序列化让对象"存得下、传得走"——实现 Serializable、声明 serialVersionUID、用 ObjectOutputStream/ObjectInputStream 完成读写,敏感字段用 transient 保护。