
粉丝提问:
Java 序列化与反序列化的核心是什么?如何实现高效的序列化?JDK 8、17 和 21 中有哪些实用的优化技巧?
本文将深入解析 Java 序列化与反序列化的基本原理、常见实现方式以及 JDK 8、17 和 21 的优化技巧,结合代码案例提供最佳实践,帮助你构建高效、可靠的序列化方案。
将对象的状态转换为字节流,用于持久化存储或网络传输。
将字节流还原为对象,恢复原始数据。
Serializable 接口Java 提供了 Serializable 接口作为标准序列化方案。
import java.io.*;
class User implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
}
public class SerializationDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
User user = new User("Alice", 25);
// 序列化
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser"))) {
oos.writeObject(user);
}
// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser"))) {
User deserializedUser = (User) ois.readObject();
System.out.println("反序列化结果:" + deserializedUser);
}
}
}输出结果:
反序列化结果:User{name='Alice', age=25}通过实现 readObject 和 writeObject 方法,自定义序列化逻辑。
class SecureUser implements Serializable {
private static final long serialVersionUID = 1L;
private transient String password; // 不序列化
private String username;
public SecureUser(String username, String password) {
this.username = username;
this.password = password;
}
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
oos.writeObject(password != null ? password.hashCode() : null); // 存储密码的哈希值
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
password = null; // 密码在反序列化时仍为空
}
@Override
public String toString() {
return "SecureUser{username='" + username + "', password='" + password + "'}";
}
}使用第三方库(如 Kryo、Protostuff)或 Java 原生的 Externalizable 接口代替。
Externalizableimport java.io.*;
class ExternalUser implements Externalizable {
private String name;
private int age;
public ExternalUser() {} // 必须提供无参构造器
public ExternalUser(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(name);
out.writeInt(age);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = in.readUTF();
age = in.readInt();
}
@Override
public String toString() {
return "ExternalUser{name='" + name + "', age=" + age + "}";
}
}serialVersionUID 的管理默认 serialVersionUID 生成方式可能导致序列化兼容性问题。
显式声明 serialVersionUID,确保版本兼容。
Record 类自动实现 Serializable,简化数据类的序列化。
import java.io.*;
record RecordUser(String name, int age) implements Serializable {}
public class RecordSerializationDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
RecordUser user = new RecordUser("Bob", 30);
// 序列化
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("recordUser.ser"))) {
oos.writeObject(user);
}
// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("recordUser.ser"))) {
RecordUser deserializedUser = (RecordUser) ois.readObject();
System.out.println("反序列化结果:" + deserializedUser);
}
}
}java.io.Serial 注解JDK 16 起引入了 @Serial 注解,用于标记序列化相关的方法,增强代码可读性。
ObjectOutputStream 的缓冲使用 BufferedOutputStream 包装流,减少 I/O 操作。
try (ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream("user.ser")))) {
oos.writeObject(new User("Alice", 25));
}使用 Java Flight Recorder(JFR) 或 VisualVM 分析对象序列化的热点和瓶颈。
java -XX:StartFlightRecording=duration=60s,filename=recording.jfr MyApp避免深层嵌套的对象结构,改用扁平化的设计。
A:只要 serialVersionUID 一致且类的结构未变,序列化是兼容的。
A:
ObjectInputFilter:过滤不安全的对象。ObjectInputFilterObjectInputFilter filter = ObjectInputFilter.Config.createFilter("com.example.*;!*");
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser"))) {
ois.setObjectInputFilter(filter);
User user = (User) ois.readObject();
}序列化的优化要点:
serialVersionUID,确保版本兼容。ObjectInputFilter 确保反序列化安全性。