什么是序列化(Serialization)?
序列化(Serialization) 是指把对象转换成可以存储或传输的格式,这样数据可以在不同系统之间传输,或者存储到文件、数据库、缓存等地方。
反序列化(Deserialization) 则是序列化的逆过程,即把存储或传输的字节数据转换回对象。
为什么需要序列化?
在 Java 里,程序运行时的对象(如 User 类的实例)是存储在 内存(JVM 堆) 里的,而我们经常需要:
- 将对象存入文件、数据库(如 MySQL、Redis)。
- 在网络上传输对象(如 Web API、RPC 调用)。
- 缓存对象(如 Redis、Ehcache)。
- 跨进程通信(如 Java RMI)。
但 Java 的对象在内存中是复杂的 数据结构,不能直接存到文件或网络中。
所以,我们需要把对象转换成可以存储或传输的格式,比如:
- JSON
- XML
- 二进制流
- Protocol Buffers(Protobuf)
这个转换的过程就叫 序列化,反过来恢复对象的过程就是 反序列化。
Java 序列化方式
1. Java 自带序列化(JDK 原生)
Java 提供了 Serializable 接口,可以让对象支持 二进制序列化:
示例
import java.io.*;
// 让类实现 Serializable 接口
class User implements Serializable {
private static final long serialVersionUID = 1L; // 用于版本控制
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
}
public class TestSerialization {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 序列化:对象写入文件
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser"));
User user = new User("张三", 25);
oos.writeObject(user);
oos.close();
// 反序列化:从文件读取对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser"));
User deserializedUser = (User) ois.readObject();
ois.close();
System.out.println("反序列化后的对象:" + deserializedUser);
}
}
输出:
反序列化后的对象:User{name='张三', age=25}
这里
Serializable自动将对象转换成二进制,并存入user.ser文件。
2. JSON 序列化(Spring Boot 常用)
在 Web 开发中,最常见的序列化方式是 JSON,因为 JSON 是可读性强、跨平台支持的格式。
Spring Boot 默认使用 Jackson 库来进行 JSON 序列化。
示例
import com.fasterxml.jackson.databind.ObjectMapper;
class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
}
public class JsonSerialization {
public static void main(String[] args) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
User user = new User("张三", 25);
// 序列化:对象 -> JSON
String jsonString = objectMapper.writeValueAsString(user);
System.out.println("JSON 序列化:" + jsonString);
// 反序列化:JSON -> 对象
User deserializedUser = objectMapper.readValue(jsonString, User.class);
System.out.println("反序列化后的对象:" + deserializedUser.getName());
}
}
输出:
JSON 序列化:{"name":"张三","age":25}
反序列化后的对象:张三
这里
ObjectMapper自动把对象转换成 JSON,并可以反序列化回 Java 对象。
3. XML 序列化
如果要把对象转换成 XML(用于 Web Service、配置文件等),可以用 javax.xml.bind.JAXB:
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.io.StringWriter;
@XmlRootElement
class User {
private String name;
private int age;
@XmlElement
public String getName() { return name; }
@XmlElement
public int getAge() { return age; }
public User() {} // JAXB 需要无参构造器
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
public class XmlSerialization {
public static void main(String[] args) throws JAXBException {
JAXBContext context = JAXBContext.newInstance(User.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
User user = new User("张三", 25);
StringWriter writer = new StringWriter();
marshaller.marshal(user, writer);
System.out.println(writer.toString());
}
}
输出:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<user>
<name>张三</name>
<age>25</age>
</user>
这里
JAXB把对象转换成 XML 格式,常用于 Web Service。
4. Protobuf 序列化(高性能)
Google 提供的 Protocol Buffers(Protobuf) 是一种高效的 二进制序列化格式,适用于高性能 RPC 通信:
- 比 JSON 小 10 倍
- 速度比 JSON 快 5-10 倍
- 适用于微服务、网络通信
示例
- 定义
.proto文件
syntax = "proto3";
message User {
string name = 1;
int32 age = 2;
}
- 用 Protobuf 生成 Java 类
protoc --java_out=. User.proto
- 序列化 / 反序列化
User user = User.newBuilder().setName("张三").setAge(25).build();
byte[] data = user.toByteArray(); // 序列化
User newUser = User.parseFrom(data); // 反序列化
总结
| 序列化方式 | 优点 | 缺点 | 应用场景 |
|---|---|---|---|
Java Serializable | 自带、简单 | 体积大、不跨语言 | 本地存储、缓存 |
| JSON(Jackson) | 人类可读、跨语言 | 体积大,速度慢 | API 传输、日志存储 |
| XML(JAXB) | 结构清晰、标准化 | 体积大 | Web Service、配置文件 |
| Protobuf | 体积小、速度快 | 需要 .proto 定义 | 高性能 RPC、微服务 |
推荐:
- Web 开发用 JSON(Jackson)。
- 轻量级网络通信用 Protobuf。
你在 Spring Boot 里用的 @JsonSerialize 就是 基于 JSON 的序列化,脱敏序列化本质上就是 在序列化过程中修改数据!
Java 里的对象本质上确实是 二进制数据,但它们在 JVM 内存中存储的格式和我们需要存储或传输的格式是不一样的,所以仍然需要序列化。
为什么 Java 对象是二进制的,还要序列化?
1. JVM 内存中的对象格式和存储/传输的格式不同
- 在 Java 里,对象在内存中的存储方式是 JVM 专用的格式,包含:
- 对象头(Object Header)
- 对象的字段(Heap Data)
- 对象方法表(Method Table)
- 但是,当我们要 存储到文件/数据库 或 通过网络传输 时,必须转换成通用格式,比如:
- JSON(可读性强)
- XML(标准化)
- Protobuf(高效传输)
- 二进制流(Java
Serializable)
所以,序列化的目的是将 JVM 内存中的对象转换成可以长期存储或跨系统传输的格式。
2. Java 的内存对象地址和结构不能直接用于存储或传输
- 在 Java 里,所有对象都存储在 堆内存 中,对象的引用是内存地址,但这些地址在不同的 JVM 或计算机上都是无效的。
- 例如:
User user1 = new User("张三", 25); User user2 = user1;user2只是user1的引用(指针),它们指向 同一个内存地址,但这个地址在不同机器上是无效的。 - 如果你直接把 Java 内存地址保存到文件里,下次程序重启,地址会变成无效的,所以必须用序列化把对象转换成可存储的数据格式。
3. Java 对象可能包含复杂的数据结构
- Java 的对象不只是简单的二进制数据,还可能包含:
- 集合(List、Map)
- 继承关系
- 循环引用
- 自定义类
例如,下面的 User 对象里有一个 Address 对象:
class Address {
String city;
}
class User {
String name;
Address address;
}
如果直接把 User 作为二进制存储,它的 address 可能是一个内存地址,无法恢复成原始对象。
但是,序列化可以把 address 变成标准数据格式:
{
"name": "张三",
"address": {
"city": "北京"
}
}
这样,不管在哪台机器上都可以正确反序列化。
4. 序列化让 Java 对象可以跨语言、跨平台使用
如果没有序列化,不同的编程语言之间无法直接理解 Java 对象。
示例:
- Java 服务器 需要把
User发送给 Python 客户端。 - 如果没有序列化,Java 只能传递二进制的 JVM 对象,Python 无法理解。
- 但如果用 JSON 或 Protobuf 进行序列化,Python 就能解析:
- Java 序列化 JSON:
{"name": "张三", "age": 25} - Python 反序列化 JSON:
import json data = '{"name": "张三", "age": 25}' user = json.loads(data) print(user["name"]) # 张三
- Java 序列化 JSON:
结论:序列化让 Java 对象可以跨平台使用,不受 JVM 限制。
总结
| 问题 | 解释 |
|---|---|
| Java 对象是二进制的,为什么还要序列化? | Java 内存对象的格式是 JVM 专用的,不能直接存储或传输。 |
| 为什么不能直接存对象的二进制? | 因为 Java 对象包含 指针、内存地址、方法表,不同 JVM 不兼容。 |
| 为什么 JSON / XML / Protobuf 更好? | 因为它们是 标准格式,可以跨语言、跨平台存储和传输。 |
| 序列化的好处? | 让对象可以存入数据库、文件、网络传输,并能恢复回来。 |
所以,序列化的本质就是把 JVM 里的“内存对象”转换成“可存储和传输的标准格式”,以便数据持久化和跨平台使用!
暂时没有回复