Java中的序列化(Serialization)与反序列化(Deserialization)是用于将对象转换为字节流(或文本格式)以便存储或传输,以及将字节流恢复为对象的过程。以下是详细说明:
1. 核心概念
- 序列化:将对象转换为字节流(或其他格式,如JSON、XML),使其可以保存到文件、数据库或通过网络传输。
- 反序列化:将字节流(或文本格式)还原为内存中的对象。
2. 实现方式
Java原生支持序列化,主要通过以下接口和类实现:
Serializable
接口:标记接口(无方法),表示类可以被序列化。ObjectOutputStream
:将对象写入字节流。ObjectInputStream
:从字节流读取对象。
示例代码
import java.io.*;
// 实现 Serializable 接口
class Person implements Serializable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
public class SerializationDemo {
public static void main(String[] args) {
// 序列化
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
Person person = new Person("Alice", 30);
oos.writeObject(person);
} catch (IOException e) {
e.printStackTrace();
}
// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
Person restoredPerson = (Person) ois.readObject();
System.out.println(restoredPerson); // 输出:Person{name='Alice', age=30}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
3. 关键细节
3.1 transient
关键字
- 标记字段不参与序列化(如敏感信息)。
private transient String password;
3.2 serialVersionUID
- 显式声明序列化版本号,避免类结构变化导致反序列化失败。
private static final long serialVersionUID = 1L;
- 如果类没有显式声明 serialVersionUID,Java会根据类的结构自动生成一个版本号。在反序列化时,如果对象新增一个字段,此时Java会检测到 serialVersionUID 不一致,抛出 InvalidClassException 异常。
3.3 自定义序列化
- 通过重写
writeObject()
和readObject()
方法控制序列化过程。
private void writeObject(ObjectOutputStream out) throws IOException {
// 自定义序列化逻辑
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
// 自定义反序列化逻辑
}
3.4 Externalizable
接口
- 提供更精细的序列化控制(需实现
writeExternal()
和readExternal()
方法)。 - 比
Serializable
更高效,但需要手动处理字段。
4. 常见问题与注意事项
- 版本兼容性:
- 修改类结构(如增删字段)可能导致反序列化失败,显式声明
serialVersionUID
可缓解。
- 修改类结构(如增删字段)可能导致反序列化失败,显式声明
- 安全性:
- 反序列化可能执行恶意代码,需确保数据来源可信。
- 性能:
- Java原生序列化效率较低,对性能敏感的场景可考虑其他格式(如JSON、Protobuf)。
- 跨语言支持:
- Java原生序列化仅适用于Java,跨语言通信建议使用JSON、XML或二进制协议(如Avro、Thrift)。
5. 替代方案
- JSON序列化:使用库如
Jackson
、Gson
。// 使用 Jackson 示例 ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(person); Person restoredPerson = mapper.readValue(json, Person.class);
- XML序列化:使用
JAXB
(Java Architecture for XML Binding)。 - 二进制协议:如
Protocol Buffers
、Apache Avro
。
6. 应用场景
- 对象持久化(保存到文件或数据库)。
- 远程方法调用(RPC,如Java RMI)。
- 分布式缓存(如Redis存储对象)。
- 网络通信(如HTTP接口传输JSON)。
总结
Java原生序列化简单易用,但需注意版本控制和安全性。在实际开发中,根据场景选择合适的方式(如JSON、Protobuf)能提升效率和跨语言兼容性。