Java中的序列化和反序列化使用及解读
作者:晚夜微雨问海棠呀
文章总结了Java中序列化和反序列化的概念,包括序列化的用途、实现方式和注意事项,此外,还介绍了其他几种常见的序列化框架,如JSON序列化、ProtocolBuffers和Kryo,并给出了最佳实践和使用场景建议
基本概念
序列化(Serialization):将对象的状态信息转换为可以存储或传输的形式(如字节流)的过程。
反序列化(Deserialization):从字节流中恢复对象的过程,是序列化的逆操作。
为什么需要序列化?
- 持久化存储:将对象保存到文件或数据库中
- 网络传输:在网络上传输对象(如分布式系统、RPC调用)
- 进程间通信:在不同进程之间传递对象
- 缓存:将对象缓存到内存或磁盘
Java 序列化实现
1. 实现 Serializable 接口
import java.io.*;
// 可序列化的类
public class Person implements Serializable {
// 序列化版本号(强烈建议)
private static final long serialVersionUID = 1L;
private String name;
private int age;
// transient 关键字:该字段不会被序列化
private transient String password;
public Person(String name, int age, String password) {
this.name = name;
this.age = age;
this.password = password;
}
// getters and setters...
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + ", password='" + password + "'}";
}
}
2. 序列化操作
public class SerializationDemo {
public static void serialize(Object obj, String filename) throws IOException {
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(filename))) {
oos.writeObject(obj);
System.out.println("对象序列化成功!");
}
}
public static Object deserialize(String filename) throws IOException, ClassNotFoundException {
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(filename))) {
Object obj = ois.readObject();
System.out.println("对象反序列化成功!");
return obj;
}
}
public static void main(String[] args) {
Person person = new Person("张三", 25, "123456");
try {
// 序列化
serialize(person, "person.ser");
// 反序列化
Person deserializedPerson = (Person) deserialize("person.ser");
System.out.println("反序列化后的对象: " + deserializedPerson);
// 输出: Person{name='张三', age=25, password='null'} (password为null因为transient)
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
重要概念
1. serialVersionUID
private static final long serialVersionUID = 1L;
- 用于标识类的版本
- 如果类结构发生变化,serialVersionUID 也应改变
- 如果不指定,JVM 会自动生成,但可能导致反序列化失败
2. transient 关键字
private transient String password; // 不会被序列化
标记不需要序列化的字段,如:
- 敏感信息(密码)
- 临时数据
- 可重新计算的数据
3. 自定义序列化
public class CustomPerson implements Serializable {
private String name;
private int age;
// 自定义序列化方法
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject(); // 默认序列化
// 可以添加额外的序列化逻辑
}
// 自定义反序列化方法
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
ois.defaultReadObject(); // 默认反序列化
// 可以添加额外的反序列化逻辑
}
}
4. Externalizable 接口
import java.io.*;
public class ExternalizablePerson implements Externalizable {
private String name;
private int age;
// 必须有无参构造器
public ExternalizablePerson() {}
public ExternalizablePerson(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(name);
out.writeInt(age);
}
@Override
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException {
name = (String) in.readObject();
age = in.readInt();
}
}
序列化的注意事项
安全问题
// 反序列化可能存在安全风险 Object obj = ois.readObject(); // 可能执行恶意代码
- 反序列化不受信任的数据可能导致代码执行
- 建议使用白名单验证类
- 考虑使用安全的序列化框架
性能问题
- Java 原生序列化性能较差
- 生成的字节流较大
- 考虑使用其他序列化框架
其他序列化框架
1. JSON 序列化(Jackson)
import com.fasterxml.jackson.databind.ObjectMapper; ObjectMapper mapper = new ObjectMapper(); // 序列化为 JSON String json = mapper.writeValueAsString(person); // 从 JSON 反序列化 Person person = mapper.readValue(json, Person.class);
2. Protocol Buffers
// 需要定义 .proto 文件 // 高性能、跨语言、体积小
3. 其他框架
- Gson:Google 的 JSON 库
- Fastjson:阿里巴巴的 JSON 库
- Kryo:高性能二进制序列化
- Hessian:二进制序列化协议
最佳实践
- 始终声明 serialVersionUID
- 使用 transient 保护敏感数据
- 考虑使用 JSON 等文本格式替代二进制序列化
- 反序列化时进行数据验证
- 对于性能敏感场景,使用专业序列化框架
- 避免序列化不可信的数据
- 不要序列化包含资源(如文件句柄、数据库连接)的对象
总结
| 特性 | Java 原生序列化 | JSON 序列化 | Protocol Buffers |
|---|---|---|---|
| 可读性 | 二进制 | 文本 | 二进制 |
| 性能 | 一般 | 较慢 | 很快 |
| 跨语言 | 仅 Java | 支持 | 支持 |
| 体积 | 较大 | 较大 | 很小 |
| 使用场景 | Java 内部使用 | Web API | 高性能场景 |
选择合适的序列化方式取决于具体的使用场景、性能要求和跨语言需求。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
