Java 序列化(Serialization)
一、理论说明
1. 序列化的定义
Java 序列化是指将对象转换为字节流的过程,以便将其存储到文件、数据库或通过网络传输。反序列化则是将字节流重新转换为对象的过程。通过实现java.io.Serializable
接口,类可以被标记为可序列化的,该接口是一个标记接口,不包含任何方法。
2. 核心用途
- 对象持久化:将对象状态保存到文件或数据库,例如缓存会话信息。
- 远程通信:在网络中传输对象,例如 RMI(远程方法调用)。
- 深拷贝:通过序列化和反序列化实现对象的深拷贝。
二、实现序列化
1. 可序列化类
必须实现Serializable
接口,并定义serialVersionUID
(推荐)。
import java.io.Serializable;public class User implements Serializable {private static final long serialVersionUID = 1L; // 版本控制private String name;private int age;// 构造方法、getter/setter 略
}
2. 序列化过程
使用ObjectOutputStream
将对象写入流。
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser"))) {User user = new User("Alice", 30);oos.writeObject(user); // 序列化对象
} catch (IOException e) {e.printStackTrace();
}
3. 反序列化过程
使用ObjectInputStream
从流读取对象。
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser"))) {User user = (User) ois.readObject(); // 反序列化对象System.out.println(user.getName()); // 输出: Alice
} catch (IOException | ClassNotFoundException e) {e.printStackTrace();
}
三、关键特性
1. serialVersionUID
- 作用:确保序列化和反序列化时类的版本一致性。若版本号不匹配,会抛出
InvalidClassException
。 - 生成方式:
private static final long serialVersionUID = 42L; // 手动指定 // 或通过 IDE 自动生成(基于类结构的哈希值)
2. 瞬态字段(
transient
)被
transient
修饰的字段不会被序列化,例如敏感信息或临时数据。private transient String password; // 不会被序列化
3. 自定义序列化逻辑
通过重写
writeObject()
和readObject()
方法,可以自定义序列化过程。private void writeObject(ObjectOutputStream out) throws IOException {out.defaultWriteObject(); // 调用默认序列化out.writeUTF(name); // 自定义处理 }private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {in.defaultReadObject(); // 调用默认反序列化name = in.readUTF(); // 自定义处理 }
四、注意事项
1. 版本兼容性
- 修改类结构(如添加字段)可能导致
serialVersionUID
不匹配,需谨慎管理版本号。 -
2. 安全风险
- 反序列化时可能执行恶意代码(如 RCE 漏洞),建议只反序列化可信来源的数据。
-
3. 替代方案
- 对于复杂场景,可使用 JSON/XML 等跨语言格式(如 Gson、Jackson)替代原生序列化。
-
五、应用实例
1. 对象深拷贝
public static <T> T deepCopy(T obj) {try (ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos)) {oos.writeObject(obj);try (ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bais)) {return (T) ois.readObject();}} catch (IOException | ClassNotFoundException e) {throw new RuntimeException(e);} }
2. 序列化集合
List<User> users = Arrays.asList(new User("Alice", 25),new User("Bob", 30) );// 序列化集合 try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("users.ser"))) {oos.writeObject(users); }
-
六、面试题
- 题目:
- 答案:
-
七、自我总结
-
Java 序列化提供了一种便捷的对象持久化和传输机制,但需注意:
- 确保类实现
Serializable
接口并定义serialVersionUID
。 - 使用
transient
关键字排除敏感字段。 - 谨慎处理版本兼容性和安全风险。
- 在跨语言场景中,考虑使用 JSON/XML 等更灵活的格式。