18.Java 序列化与反序列化
18.Java 序列化与反序列化
概述
在Java中,序列化是将对象的状态信息转换为可以存储或传输的格式的过程,而反序列化则是将这种格式转换回Java对象的过程。
序列化
要使一个对象支持序列化,该对象必须实现java.io.Serializable接口。这个接口是一个标记接口,不包含任何方法,但它告诉JVM该对象可以被序列化。
反序列化
反序列化是将之前序列化的对象数据转换回Java对象的过程。这需要使用ObjectInputStream来读取序列化数据。
主要用途和功能
- 对象持久化:将对象状态保存到文件或数据库中
- 网络传输:在不同JVM之间传输对象
- 缓存机制:缓存计算结果或对象状态
- RMI(远程方法调用):Java RMI 底层使用序列化传输对象
操作步骤
- 创建项目结构:按照上述项目结构创建文件夹和文件
- 编写实体类:实现
Serializable
接口,并定义需要序列化的字段 - 编写工具类:
SerializationUtils
:包含序列化和反序列化方法
- 编写主程序:演示序列化和反序列化流程
- 运行程序:
- 编译并运行
Main
类 - 观察控制台输出,验证序列化和反序列化结果
- 编译并运行
- 检查文件:程序运行后会生成
user.ser
文件
关键点说明
- Serializable接口:Java对象必须实现此接口才能被序列化
- serialVersionUID:用于版本控制,建议显式声明
- transient关键字:标记的字段不会被序列化
- 异常处理:序列化和反序列化可能抛出
IOException
和ClassNotFoundException
项目结构
实现步骤
1. 创建实体类 (User.java)
package com.example.model;import java.io.Serializable;
import java.util.Date;/*** 用户实体类,实现Serializable接口以支持序列化*/
public class User implements Serializable {// 序列化版本UID,用于版本控制private static final long serialVersionUID = 1L;private String username;private transient String password; // transient字段不会被序列化private int age;private Date registerDate;// 构造方法public User() {}public User(String username, String password, int age, Date registerDate) {this.username = username;this.password = password;this.age = age;this.registerDate = registerDate;}// Getter和Setter方法public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Date getRegisterDate() {return registerDate;}public void setRegisterDate(Date registerDate) {this.registerDate = registerDate;}@Overridepublic String toString() {return "User{" +"username='" + username + '\'' +", age=" + age +", registerDate=" + registerDate+", 被transient 标记的字段password="+password+'}';}
}
2. 创建序列化工具类 (SerializationUtils.java)
package com.example.utils;import java.io.*;/*** 序列化和反序列化工具类*/
public class SerializationUtils {/*** 将对象序列化到文件* @param obj 要序列化的对象* @param filePath 文件路径* @throws IOException 如果发生I/O错误*/public static void serialize(Object obj, String filePath) throws IOException {try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath))) {oos.writeObject(obj);System.out.println("对象序列化成功,保存到: " + filePath);}}/*** 从文件反序列化对象* @param filePath 文件路径* @return 反序列化后的对象* @throws IOException 如果发生I/O错误* @throws ClassNotFoundException 如果类未找到*/public static Object deserialize(String filePath) throws IOException, ClassNotFoundException {try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath))) {Object obj = ois.readObject();System.out.println("对象反序列化成功,从: " + filePath);return obj;}}
}
4. 创建主程序 (Main.java)
package com.example;import com.example.model.User;
import com.example.utils.SerializationUtils;import java.io.IOException;
import java.util.Date;/*** 主程序,演示序列化和反序列化操作*/
public class Main {// 序列化文件路径private static final String SERIALIZED_FILE = "user.ser";public static void main(String[] args) throws IOException, ClassNotFoundException{// 1. 创建用户对象User user = new User("john_doe", "securePassword123", 30, new Date());System.out.println("原始对象: " + user);// 2. 序列化对象到文件SerializationUtils.serialize(user, SERIALIZED_FILE);// 3. 反序列化对象User deserializedUser = (User) SerializationUtils.deserialize(SERIALIZED_FILE);System.out.println("反序列化后的对象: " + deserializedUser);// 注意: password字段被标记为transient,不会被序列化System.out.println("原始对象密码: " + user.getPassword());System.out.println("反序列化对象密码: " + deserializedUser.getPassword()); // 输出null}
}
运行结果
生成了序列化结果的文件,user.ser
安全注意事项
- 反序列化漏洞:Java反序列化存在安全风险,不应反序列化不可信来源的数据
- 白名单验证:在实际应用中,应对反序列化的类进行白名单验证
- 替代方案:考虑使用JSON、XML等格式替代Java原生序列化