Java序列化与反序列化
目录
- 一、基本概念
- 1.1 什么是序列化与反序列化
- 1.2 为什么需要序列化
- 1.3 序列化的应用场景
- 二、Java序列化机制
- 2.1 Serializable接口
- 2.2 Externalizable接口
- 2.3 序列化ID
- 2.4 序列化与继承
- 三、序列化实现
- 3.1 基本序列化
- 3.2 自定义序列化
- 3.3 序列化字段控制
- 3.4 序列化与静态字段
- 四、反序列化实现
- 4.1 基本反序列化
- 4.2 自定义反序列化
- 4.3 反序列化与构造函数
- 4.4 反序列化与单例模式
- 五、序列化格式
- 5.1 Java原生序列化
- 5.2 JSON序列化
- 5.3 XML序列化
- 5.4 其他序列化格式
- 六、性能考虑
- 6.1 序列化性能
- 6.2 序列化大小
- 6.3 序列化与内存
- 6.4 序列化与垃圾回收
- 七、安全考虑
- 7.1 序列化安全风险
- 7.2 反序列化漏洞
- 7.3 安全最佳实践
- 7.4 白名单过滤
- 八、常见问题与解决方案
- 8.1 序列化兼容性问题
- 8.2 循环引用问题
- 8.3 版本升级问题
- 8.4 性能优化问题
- 九、最佳实践
- 9.1 序列化设计原则
- 9.2 序列化与不可变性
- 9.3 序列化与设计模式
- 9.4 序列化与框架集成
一、基本概念
1.1 什么是序列化与反序列化
**序列化(Serialization)**是将对象的状态信息转换为可以存储或传输的形式的过程。在Java中,序列化是将对象转换为字节序列的过程。
**反序列化(Deserialization)**是将序列化的字节序列恢复为对象的过程。
简单来说:
- 序列化:对象 → 字节流
- 反序列化:字节流 → 对象
1.2 为什么需要序列化
序列化在以下场景中非常有用:
- 持久化存储:将对象保存到文件或数据库中
- 网络传输:在网络上传输对象(如RPC调用)
- 缓存:将对象缓存到内存或分布式缓存中
- 深拷贝:通过序列化和反序列化实现对象的深拷贝
- 会话复制:在分布式系统中复制会话状态
1.3 序列化的应用场景
- 分布式系统:微服务架构中的服务间通信
- 缓存系统:Redis、Memcached等缓存系统
- 消息队列:Kafka、RabbitMQ等消息队列系统
- Web应用:会话管理、Cookie存储
- 大数据处理:Hadoop、Spark等大数据框架
- 游戏开发:游戏状态保存和加载
二、Java序列化机制
2.1 Serializable接口
Java中最基本的序列化机制是通过实现Serializable
接口来实现的:
import java.io.Serializable;public class Person implements Serializable {private String name;private int age;// 构造函数、getter和setter方法
}
Serializable
是一个标记接口,没有定义任何方法。实现这个接口的类可以被序列化。
2.2 Externalizable接口
Externalizable
接口提供了更细粒度的序列化控制:
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;public class Person implements Externalizable {private String name;private int age;// 必须有无参构造函数public Person() {// 默认构造函数}// 自定义序列化@Overridepublic void writeExternal(ObjectOutput out) throws IOException {out.writeObject(name);out.writeInt(age);}// 自定义反序列化@Overridepublic void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {name = (String) in.readObject();age = in.readInt();}// 构造函数、getter和setter方法
}
与Serializable
相比,Externalizable
提供了完全自定义序列化和反序列化过程的能力。
2.3 序列化ID
序列化ID(serialVersionUID)用于版本控制,确保序列化和反序列化的类版本兼容:
import java.io.Serializable;public class Person implements Serializable {private static final long serialVersionUID = 1L;private String name;private int age;// 构造函数、getter和setter方法
}
如果不显式定义serialVersionUID
,JVM会根据类的结构自动生成一个。但是,当类的结构发生变化时,自动生成的ID也会变化,这可能导致反序列化失败。因此,建议显式定义serialVersionUID
。
2.4 序列化与继承
序列化与继承的关系:
- 父类可序列化,子类自动可序列化
- 父类不可序列化,子类可序列化:子类需要负责序列化父类的状态
- 子类可序列化,父类构造函数有参数:父类必须有无参构造函数
示例:
// 父类不可序列化
public class Animal {protected String species;public Animal(String species) {this.species = species;}// 必须有无参构造函数public Animal() {this.species = "Unknown";}
}// 子类可序列化
public class Dog extends Animal implements Serializable {private static final long serialVersionUID = 1L;private String name;public Dog(String species, String name) {super(species);this.name = name;}// 自定义序列化private void writeObject(java.io.ObjectOutputStream out) throws IOException {out.defaultWriteObject();out.writeObject(species); // 序列化父类字段}// 自定义反序列化private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {in.defaultReadObject();species = (String) in.readObject(); // 反序列化父类字段}
}
三、序列化实现
3.1 基本序列化
使用ObjectOutputStream
进行基本序列化:
import java.io.*;public class SerializationExample {public static void main(String[] args) {Person person = new Person("张三", 30);// 序列化try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {oos.writeObject(person);System.out.println("对象已序列化");} catch (IOException e) {e.printStackTrace();}}
}
3.2 自定义序列化
通过实现writeObject
和readObject
方法来自定义序列化过程:
import java.io.*;public class Person implements Serializable {private static final long serialVersionUID = 1L;private String name;private int age;private transient String password; // 不序列化密码public Person(String name, int age, String password) {this.name = name;this.age = age;this.password = password;}// 自定义序列化private void writeObject(ObjectOutputStream out) throws IOException {out.defaultWriteObject(); // 调用默认序列化// 对密码进行加密后再序列化String encryptedPassword = encryptPassword(password);out.writeObject(encryptedPassword);}// 自定义反序列化private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {in.defaultReadObject(); // 调用默认反序列化// 解密密码String encryptedPassword = (String) in.readObject();this.password = decryptPassword(encryptedPassword);}// 加密和解密方法(示例)private String encryptPassword(String password) {// 实际应用中应使用安全的加密算法return "encrypted_" + password;}private String decryptPassword(String encryptedPassword) {// 实际应用中应使用安全的解密算法return encryptedPassword.substring("encrypted_".length());}// getter和setter方法
}
3.3 序列化字段控制
使用transient
关键字控制字段是否序列化:
import java.io.Serializable;public class User implements Serializable {private static final long serialVersionUID = 1L;private String username;private transient String password; // 不序列化private transient int loginCount; // 不序列化,可以在反序列化后重新计算public User(String username, String password) {this.username = username;this.password = password;this.loginCount = 0;}// 自定义反序列化,重新计算loginCountprivate void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {in.defaultReadObject();this.loginCount = 0; // 重置登录次数}// getter和setter方法
}
3.4 序列化与静态字段
静态字段不会被序列化,因为静态字段属于类而不是对象:
import java.io.Serializable;public class Counter implements Serializable {private static final long serialVersionUID = 1L;private int count;private static int totalCount = 0; // 静态字段不会被序列化public Counter() {count = 0;totalCount++;}public void increment() {count++;totalCount++;}public int getCount() {return count;}public static int getTotalCount() {return totalCount;}
}
四、反序列化实现
4.1 基本反序列化
使用ObjectInputStream
进行基本反序列化:
import java.io.*;public class DeserializationExample {public static void main(String[] args) {// 反序列化try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {Person person = (Person) ois.readObject();System.out.println("反序列化成功: " + person.getName() + ", " + person.getAge());} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}}
}
4.2 自定义反序列化
通过实现readObject
方法来自定义反序列化过程:
import java.io.*;public class Person implements Serializable {private static final long serialVersionUID = 1L;private String name;private int age;private String email;public Person(String name, int age) {this.name = name;this.age = age;}// 自定义反序列化private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {in.defaultReadObject();// 如果email为null,设置默认值if (email == null) {email = name.toLowerCase().replace(" ", ".") + "@example.com";}}// getter和setter方法
}
4.3 反序列化与构造函数
反序列化过程中不会调用类的构造函数,而是使用默认的无参构造函数:
import java.io.*;public class Person implements Serializable {private static final long serialVersionUID = 1L;private String name;private int age;public Person(String name, int age) {System.out.println("构造函数被调用");this.name = name;this.age = age;}// 必须有无参构造函数public Person() {System.out.println("无参构造函数被调用");}// getter和setter方法
}
4.4 反序列化与单例模式
反序列化可能会破坏单例模式,因为反序列化会创建新的实例:
import java.io.*;public class Singleton implements Serializable {private static final long serialVersionUID = 1L;private static Singleton instance;private Singleton() {// 私有构造函数}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}// 防止反序列化创建新实例private Object readResolve() {return getInstance();}
}
readResolve
方法会在反序列化后被调用,可以用来返回单例实例,从而防止反序列化创建新实例。
五、序列化格式
5.1 Java原生序列化
Java原生序列化使用ObjectOutputStream
和ObjectInputStream
:
// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.ser"));
oos.writeObject(object);
oos.close();// 反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.ser"));
Object object = ois.readObject();
ois.close();
优点:
- 简单易用
- 自动处理对象图
- 支持循环引用
缺点:
- 序列化后的数据较大
- 序列化和反序列化速度较慢
- 与Java语言强耦合
5.2 JSON序列化
使用Jackson、Gson等库进行JSON序列化:
// 使用Jackson
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(object);
Object obj = mapper.readValue(json, Object.class);// 使用Gson
Gson gson = new Gson();
String json = gson.toJson(object);
Object obj = gson.fromJson(json, Object.class);
优点:
- 人类可读
- 语言无关
- 广泛支持
- 序列化后的数据较小
缺点:
- 不支持复杂对象图
- 不支持循环引用
- 性能不如二进制格式
5.3 XML序列化
使用JAXB、XStream等库进行XML序列化:
// 使用JAXB
JAXBContext context = JAXBContext.newInstance(Person.class);
Marshaller marshaller = context.createMarshaller();
marshaller.marshal(person, new File("person.xml"));Unmarshaller unmarshaller = context.createUnmarshaller();
Person person = (Person) unmarshaller.unmarshal(new File("person.xml"));
优点:
- 人类可读
- 语言无关
- 支持复杂结构
- 支持验证
缺点:
- 序列化后的数据较大
- 序列化和反序列化速度较慢
- 配置复杂
5.4 其他序列化格式
-
Protocol Buffers:
// 定义.proto文件 message Person {required string name = 1;required int32 age = 2; }// 使用生成的类 Person person = Person.newBuilder().setName("张三").setAge(30).build(); byte[] bytes = person.toByteArray(); Person parsed = Person.parseFrom(bytes);
-
Apache Avro:
// 定义schema String schema = "{\"type\":\"record\",\"name\":\"Person\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"age\",\"type\":\"int\"}]}";// 使用Avro DatumWriter<GenericRecord> writer = new GenericDatumWriter<>(new Schema.Parser().parse(schema)); ByteArrayOutputStream output = new ByteArrayOutputStream(); JsonEncoder encoder = EncoderFactory.get().jsonEncoder(new Schema.Parser().parse(schema), output); writer.write(record, encoder);
-
MessagePack:
// 使用MessagePack MessagePack msgpack = new MessagePack(); byte[] bytes = msgpack.write(object); Object obj = msgpack.read(bytes);
六、性能考虑
6.1 序列化性能
不同序列化方式的性能比较:
- Java原生序列化:中等性能,适合简单对象
- JSON序列化:较低性能,但人类可读
- Protocol Buffers:高性能,适合大规模数据
- MessagePack:高性能,二进制格式
- Kryo:极高性能,但仅支持Java
性能优化技巧:
// 使用Kryo进行高性能序列化
Kryo kryo = new Kryo();
kryo.register(Person.class);
ByteArrayOutputStream output = new ByteArrayOutputStream();
Output output = new Output(output);
kryo.writeObject(output, person);
output.close();
byte[] bytes = output.toByteArray();// 反序列化
Kryo kryo = new Kryo();
kryo.register(Person.class);
Input input = new Input(bytes);
Person person = kryo.readObject(input, Person.class);
input.close();
6.2 序列化大小
不同序列化方式的序列化大小比较:
- Java原生序列化:较大,包含类信息
- JSON序列化:中等,人类可读
- Protocol Buffers:较小,二进制格式
- MessagePack:较小,二进制格式
- Kryo:最小,但仅支持Java
减小序列化大小的技巧:
// 使用transient排除不需要序列化的字段
public class Person implements Serializable {private String name;private transient String password; // 不序列化private transient List<String> temporaryData; // 不序列化临时数据
}// 使用自定义序列化控制序列化内容
private void writeObject(ObjectOutputStream out) throws IOException {out.defaultWriteObject();// 只序列化必要的数据out.writeObject(essentialData);
}
6.3 序列化与内存
序列化过程中的内存使用:
- 对象图遍历:序列化时会遍历整个对象图
- 临时对象创建:序列化过程中会创建临时对象
- 内存泄漏风险:处理大对象时可能导致内存问题
内存优化技巧:
// 使用流式处理避免一次性加载大对象
public void processLargeObject(InputStream in) throws IOException {try (ObjectInputStream ois = new ObjectInputStream(in)) {while (true) {try {Person person = (Person) ois.readObject();processPerson(person);} catch (EOFException e) {break;}}} catch (ClassNotFoundException e) {throw new IOException(e);}
}// 使用分块序列化
public void serializeLargeObject(Object obj, OutputStream out) throws IOException {try (ObjectOutputStream oos = new ObjectOutputStream(out)) {// 分块序列化for (Chunk chunk : getChunks(obj)) {oos.writeObject(chunk);oos.flush(); // 及时刷新,避免内存累积}}
}
6.4 序列化与垃圾回收
序列化过程中的垃圾回收考虑:
- 临时对象:序列化过程中创建的临时对象会增加GC压力
- 大对象:序列化大对象可能导致内存压力
- 缓存:序列化缓存可能导致内存泄漏
垃圾回收优化技巧:
// 使用对象池减少临时对象创建
public class ObjectPool<T> {private final Queue<T> pool = new ConcurrentLinkedQueue<>();public T borrowObject() {T obj = pool.poll();return obj != null ? obj : createNew();}public void returnObject(T obj) {pool.offer(obj);}private T createNew() {// 创建新对象}
}// 使用软引用或弱引用缓存序列化结果
public class SerializationCache {private final Map<Object, SoftReference<byte[]>> cache = new ConcurrentHashMap<>();public byte[] serialize(Object obj) {SoftReference<byte[]> ref = cache.get(obj);byte[] bytes = ref != null ? ref.get() : null;if (bytes == null) {bytes = doSerialize(obj);cache.put(obj, new SoftReference<>(bytes));}return bytes;}
}
七、安全考虑
7.1 序列化安全风险
序列化过程中的安全风险:
- 敏感数据泄露:序列化可能包含敏感信息
- 反序列化漏洞:恶意序列化数据可能导致安全问题
- 远程代码执行:反序列化可能执行任意代码
安全风险示例:
// 不安全的序列化
public class User implements Serializable {private String username;private String password; // 敏感信息// 构造函数、getter和setter
}// 安全的序列化
public class User implements Serializable {private String username;private transient String password; // 不序列化敏感信息// 构造函数、getter和setter
}
7.2 反序列化漏洞
Java反序列化漏洞(如Apache Commons Collections漏洞):
// 不安全的反序列化
ObjectInputStream ois = new ObjectInputStream(input);
Object obj = ois.readObject(); // 可能执行恶意代码// 安全的反序列化
public class SecureObjectInputStream extends ObjectInputStream {public SecureObjectInputStream(InputStream in) throws IOException {super(in);enableResolveObject(true);}@Overrideprotected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {// 白名单验证String className = desc.getName();if (!isAllowedClass(className)) {throw new InvalidClassException("Unauthorized deserialization attempt", className);}return super.resolveClass(desc);}private boolean isAllowedClass(String className) {// 实现白名单验证return ALLOWED_CLASSES.contains(className);}
}
7.3 安全最佳实践
序列化安全最佳实践:
-
避免序列化敏感数据:
public class SecureObject implements Serializable {private String publicData;private transient String sensitiveData; // 不序列化敏感数据 }
-
使用自定义序列化控制敏感数据:
private void writeObject(ObjectOutputStream out) throws IOException {out.defaultWriteObject();// 加密敏感数据String encryptedData = encrypt(sensitiveData);out.writeObject(encryptedData); }private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {in.defaultReadObject();// 解密敏感数据String encryptedData = (String) in.readObject();sensitiveData = decrypt(encryptedData); }
-
实现白名单过滤:
public class SecureDeserializer {private static final Set<String> ALLOWED_CLASSES = new HashSet<>();static {ALLOWED_CLASSES.add("java.lang.String");ALLOWED_CLASSES.add("java.util.ArrayList");ALLOWED_CLASSES.add("com.example.SafeClass");}public static Object deserialize(byte[] data) throws IOException, ClassNotFoundException {try (ObjectInputStream ois = new SecureObjectInputStream(new ByteArrayInputStream(data))) {return ois.readObject();}} }
7.4 白名单过滤
实现白名单过滤以防止反序列化漏洞:
import java.io.*;
import java.util.*;public class WhitelistDeserializer {private static final Set<String> ALLOWED_CLASSES = new HashSet<>();static {// 添加允许的类ALLOWED_CLASSES.add("java.lang.String");ALLOWED_CLASSES.add("java.lang.Integer");ALLOWED_CLASSES.add("java.util.ArrayList");ALLOWED_CLASSES.add("java.util.HashMap");ALLOWED_CLASSES.add("com.example.SafeClass");}public static Object deserialize(byte[] data) throws IOException, ClassNotFoundException {try (ObjectInputStream ois = new WhitelistObjectInputStream(new ByteArrayInputStream(data))) {return ois.readObject();}}private static class WhitelistObjectInputStream extends ObjectInputStream {public WhitelistObjectInputStream(InputStream in) throws IOException {super(in);enableResolveObject(true);}@Overrideprotected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {String className = desc.getName();if (!isAllowedClass(className)) {throw new InvalidClassException("Unauthorized deserialization attempt", className);}return super.resolveClass(desc);}private boolean isAllowedClass(String className) {return ALLOWED_CLASSES.contains(className);}}
}
八、常见问题与解决方案
8.1 序列化兼容性问题
序列化兼容性问题通常发生在类结构变化时:
// 原始类
public class Person implements Serializable {private static final long serialVersionUID = 1L;private String name;private int age;// 构造函数、getter和setter
}// 修改后的类
public class Person implements Serializable {private static final long serialVersionUID = 1L;private String name;private int age;private String email; // 新增字段// 构造函数、getter和setter
}
解决方案:
-
保持serialVersionUID不变:
private static final long serialVersionUID = 1L; // 保持不变
-
使用自定义序列化处理兼容性:
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {in.defaultReadObject();// 处理新增字段if (email == null) {email = "default@example.com";} }
-
使用版本控制:
public class Person implements Serializable {private static final long serialVersionUID = 1L;private int version = 1; // 版本字段private String name;private int age;private String email;private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {in.defaultReadObject();if (version < 2) {email = "default@example.com";version = 2;}} }
8.2 循环引用问题
序列化循环引用可能导致栈溢出:
public class Node implements Serializable {private String data;private Node next;public Node(String data) {this.data = data;}public void setNext(Node next) {this.next = next;}
}// 创建循环引用
Node node1 = new Node("A");
Node node2 = new Node("B");
node1.setNext(node2);
node2.setNext(node1); // 循环引用
解决方案:
-
使用transient排除循环引用:
public class Node implements Serializable {private String data;private transient Node next; // 不序列化next字段// 其他代码 }
-
使用自定义序列化处理循环引用:
private void writeObject(ObjectOutputStream out) throws IOException {out.defaultWriteObject();// 使用ID引用代替直接引用if (next != null) {out.writeBoolean(true);out.writeObject(next.data);} else {out.writeBoolean(false);} }private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {in.defaultReadObject();// 重建引用关系if (in.readBoolean()) {String nextData = (String) in.readObject();next = new Node(nextData);} }
8.3 版本升级问题
版本升级时的序列化兼容性问题:
// 原始版本
public class Product implements Serializable {private static final long serialVersionUID = 1L;private String name;private double price;// 构造函数、getter和setter
}// 升级版本
public class Product implements Serializable {private static final long serialVersionUID = 1L;private String name;private double price;private String category; // 新增字段private int stock; // 新增字段// 构造函数、getter和setter
}
解决方案:
-
使用版本控制:
public class Product implements Serializable {private static final long serialVersionUID = 1L;private int version = 1;private String name;private double price;private String category;private int stock;private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {in.defaultReadObject();if (version < 2) {category = "Uncategorized";stock = 0;version = 2;}} }
-
使用迁移工具:
public class ProductMigration {public static Product migrate(Product oldProduct) {Product newProduct = new Product();newProduct.setName(oldProduct.getName());newProduct.setPrice(oldProduct.getPrice());newProduct.setCategory("Uncategorized");newProduct.setStock(0);return newProduct;} }
8.4 性能优化问题
序列化性能优化:
-
使用更高效的序列化库:
// 使用Kryo Kryo kryo = new Kryo(); kryo.register(Person.class); ByteArrayOutputStream output = new ByteArrayOutputStream(); Output output = new Output(output); kryo.writeObject(output, person); output.close();
-
减少序列化数据量:
public class OptimizedPerson implements Serializable {private static final long serialVersionUID = 1L;private String name;private int age;private transient List<String> temporaryData; // 不序列化临时数据// 只序列化必要的数据private void writeObject(ObjectOutputStream out) throws IOException {out.defaultWriteObject();// 只序列化必要的数据} }
-
使用流式处理:
public void processLargeData(InputStream in) throws IOException {try (ObjectInputStream ois = new ObjectInputStream(in)) {while (true) {try {Person person = (Person) ois.readObject();processPerson(person);} catch (EOFException e) {break;}}} catch (ClassNotFoundException e) {throw new IOException(e);} }
九、最佳实践
9.1 序列化设计原则
序列化设计的最佳原则:
-
最小化序列化数据:
public class MinimalPerson implements Serializable {private static final long serialVersionUID = 1L;private String name;private int age;private transient String password; // 不序列化敏感数据private transient List<String> temporaryData; // 不序列化临时数据 }
-
使用不可变对象:
public final class ImmutablePerson implements Serializable {private static final long serialVersionUID = 1L;private final String name;private final int age;public ImmutablePerson(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;} }
-
实现自定义序列化:
private void writeObject(ObjectOutputStream out) throws IOException {out.defaultWriteObject();// 自定义序列化逻辑 }private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {in.defaultReadObject();// 自定义反序列化逻辑 }
9.2 序列化与不可变性
不可变对象与序列化的关系:
public final class ImmutablePerson implements Serializable {private static final long serialVersionUID = 1L;private final String name;private final int age;public ImmutablePerson(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}// 不需要自定义序列化,因为对象是不可变的
}
不可变对象的优点:
- 线程安全
- 简化序列化
- 避免状态变化导致的序列化问题
9.3 序列化与设计模式
序列化与常用设计模式的结合:
-
单例模式:
public class Singleton implements Serializable {private static final long serialVersionUID = 1L;private static Singleton instance;private Singleton() {// 私有构造函数}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}// 防止反序列化创建新实例private Object readResolve() {return getInstance();} }
-
工厂模式:
public class PersonFactory implements Serializable {private static final long serialVersionUID = 1L;public static Person createPerson(String name, int age) {return new Person(name, age);} }
-
代理模式:
public class PersonProxy implements Serializable {private static final long serialVersionUID = 1L;private Person person;public PersonProxy(Person person) {this.person = person;}// 代理方法public String getName() {return person.getName();}// 自定义序列化,只序列化必要的数据private void writeObject(ObjectOutputStream out) throws IOException {out.defaultWriteObject();out.writeObject(person.getName());out.writeInt(person.getAge());}// 自定义反序列化,重建代理对象private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {in.defaultReadObject();String name = (String) in.readObject();int age = in.readInt();person = new Person(name, age);} }
9.4 序列化与框架集成
序列化在常用框架中的应用:
-
Spring框架:
@Component public class SerializableBean implements Serializable {private static final long serialVersionUID = 1L;private String data;// getter和setter }
-
Hibernate/JPA:
@Entity public class Person implements Serializable {private static final long serialVersionUID = 1L;@Id@GeneratedValueprivate Long id;private String name;private int age;// getter和setter }
-
RESTful API:
@RestController @RequestMapping("/api") public class PersonController {@GetMapping("/person/{id}")public Person getPerson(@PathVariable Long id) {// 返回可序列化的Person对象return personService.getPerson(id);} }
-
消息队列:
public class MessageProducer {public void sendMessage(Person person) {// 序列化Person对象并发送到消息队列byte[] serialized = serialize(person);queue.send(serialized);}private byte[] serialize(Person person) {try (ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos)) {oos.writeObject(person);return baos.toByteArray();} catch (IOException e) {throw new RuntimeException("序列化失败", e);}} }