这个过程的核心技术叫做 序列化(Serialization),序列化就是将 Java 对象及其状态信息转换为可以存储或传输的字节流的过程,对应的,从字节流恢复成 Java 对象的过程叫做 反序列化(Deserialization)。

下面我将详细介绍几种主要的方法,从最基础到最推荐的方式。
使用 Java 原生序列化(Serializable 接口)
这是 Java 语言内置的、最基础的序列化机制,几乎所有 Java 对象都可以通过实现 java.io.Serializable 接口来完成序列化。
步骤:
-
让对象类实现
Serializable接口: 这个接口是一个标记接口,它本身不包含任何方法,它的作用是告诉 JVM 这个类的对象可以被序列化。 -
使用
ObjectOutputStream进行序列化: 将对象写入一个输出流,这个输出流可以被重定向到文件、网络套接字或字节数组。
(图片来源网络,侵删) -
使用
ObjectInputStream进行反序列化: 从输入流中读取字节,并将其转换回原始的 Java 对象。
示例代码:
定义可序列化的对象类
import java.io.Serializable;
// 1. 让类实现 Serializable 接口
public class User implements Serializable {
// 序列化版本号,用于控制版本兼容性,强烈建议添加。
private static final long serialVersionUID = 1L;
private String name;
private int age;
private transient String password; // 使用 transient 关键字标记,该字段不会被序列化
public User(String name, int age, String password) {
this.name = name;
this.age = age;
this.password = password;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", password='" + (password != null ? "******" : "null") + '\'' +
'}';
}
}
编写转换工具类
import java.io.*;
public class SerializableUtils {
/**
* 将对象序列化为字节数组
* @param obj 要序列化的对象
* @return 字节数组
* @throws IOException 如果发生 I/O 错误
*/
public static byte[] serialize(Object obj) throws IOException {
if (obj == null) {
return null;
}
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos)) {
// 2. 使用 ObjectOutputStream 将对象写入字节数组输出流
oos.writeObject(obj);
oos.flush();
return bos.toByteArray();
}
}
/**
* 从字节数组反序列化对象
* @param bytes 字节数组
* @return 反序列化后的对象
* @throws IOException 如果发生 I/O 错误
* @throws ClassNotFoundException 如果类找不到
*/
public static <T> T deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
if (bytes == null || bytes.length == 0) {
return null;
}
try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis)) {
// 3. 使用 ObjectInputStream 从字节数组输入流中读取对象
@SuppressWarnings("unchecked")
T obj = (T) ois.readObject();
return obj;
}
}
}
测试

public class Main {
public static void main(String[] args) {
User user = new User("Alice", 30, "123456");
try {
// 对象 -> byte[]
byte[] userBytes = SerializableUtils.serialize(user);
System.out.println("序列化成功,字节数组长度: " + userBytes.length);
// byte[] -> 对象
User deserializedUser = SerializableUtils.deserialize(userBytes);
System.out.println("反序列化成功: " + deserializedUser);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
优点:
- 简单易用:Java 标准库提供,无需额外依赖。
- 通用性强:适用于任何实现了
Serializable接口的 Java 对象。
缺点:
- 性能较差:序列化后的字节数组通常比较大,序列化和反序列化的速度相对较慢。
- 安全性问题:反序列化过程可能存在安全漏洞(如 Log4j 漏洞),如果反序列化的数据来源不可信,有被攻击的风险。
- 版本兼容性:当类结构(如增删字段)发生变化时,
serialVersionUID必须妥善处理,否则可能导致反序列化失败。 - 无法序列化某些对象:不能序列化
static和transient字段,也不能序列化某些系统级对象(如Thread,Socket等)。
使用第三方库(强烈推荐)
在实际开发中,由于原生序列化的种种缺点,我们更倾向于使用高性能、跨语言、更安全的第三方库,目前最主流的选择是 Jackson 和 Gson,它们通常使用 JSON 格式作为中间表示,然后再将 JSON 转换为字节数组(通常是 UTF-8 编码的字节)。
这里以 Jackson 为例,它非常流行且性能卓越。
步骤:
-
添加 Jackson 依赖: 如果你使用 Maven,在
pom.xml中添加:<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.2</version> <!-- 使用最新版本 --> </dependency> -
使用
ObjectMapper进行转换:ObjectMapper是 Jackson 库的核心类,负责对象和 JSON 之间的转换。
示例代码:
定义 POJO (Plain Old Java Object)
// POJO 不需要实现任何特殊接口,但必须有 getter/setter 或是 public 字段
public class User {
private String name;
private int age;
private String password;
// 必须有无参构造函数
public User() {
}
public User(String name, int age, String password) {
this.name = name;
this.age = age;
this.password = password;
}
// Getter 和 Setter
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", password='" + (password != null ? "******" : "null") + '\'' +
'}';
}
}
编写转换工具类
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public class JacksonUtils {
// 创建一个全局的 ObjectMapper 实例,避免重复创建的开销
private static final ObjectMapper objectMapper = new ObjectMapper();
/**
* 将对象转换为 JSON 字节数组
* @param obj 要转换的对象
* @return JSON 字节数组
*/
public static byte[] serialize(Object obj) {
if (obj == null) {
return null;
}
try {
// ObjectMapper.writeValueAsString() 也可以得到 JSON 字符串,但 writeValueAsBytes() 更直接
return objectMapper.writeValueAsBytes(obj);
} catch (JsonProcessingException e) {
throw new RuntimeException("Jackson 序列化失败", e);
}
}
/**
* 从 JSON 字节数组反序列化对象
* @param bytes JSON 字节数组
* @param clazz 目标对象的 Class 对象
* @return 反序列化后的对象
*/
public static <T> T deserialize(byte[] bytes, Class<T> clazz) {
if (bytes == null || bytes.length == 0) {
return null;
}
try {
return objectMapper.readValue(bytes, clazz);
} catch (IOException e) {
throw new RuntimeException("Jackson 反序列化失败", e);
}
}
}
测试
public class Main {
public static void main(String[] args) {
User user = new User("Bob", 25, "secret");
// 对象 -> byte[]
byte[] userBytes = JacksonUtils.serialize(user);
System.out.println("序列化成功,JSON 字节数组长度: " + userBytes.length);
System.out.println("JSON 内容: " + new String(userBytes)); // 打印 JSON 字符串
// byte[] -> 对象
User deserializedUser = JacksonUtils.deserialize(userBytes, User.class);
System.out.println("反序列化成功: " + deserializedUser);
}
}
优点:
- 高性能:速度远超原生序列化,体积更小。
- 跨语言:JSON 是一种通用的数据格式,可以被几乎所有编程语言解析,非常适合异构系统集成。
- 可读性好:JSON 文本格式清晰,便于调试和查看。
- 灵活性高:通过注解(如
@JsonIgnore)可以灵活控制哪些字段需要被序列化/反序列化。 - 安全性更高:反序列化过程比原生 Java 序列化更安全。
缺点:
- 需要引入外部库:对于小型项目可能觉得是个“负担”。
- 不能序列化所有对象:同样,
transient、static字段不会被处理,对于一些复杂的、非 POJO 的对象,可能需要额外的配置。
总结与选择建议
| 特性 | Java 原生序列化 (Serializable) |
Jackson / Gson (JSON 序列化) |
|---|---|---|
| 依赖 | 无需外部依赖 | 需要引入第三方库 |
| 格式 | Java 特定二进制格式 | JSON (文本) |
| 性能 | 较慢,体积大 | 快,体积小 |
| 可读性 | 不可读(二进制) | 可读(文本) |
| 跨语言 | 否,仅限 Java | 是,JSON 是通用标准 |
| 安全性 | 较低,有反序列化漏洞风险 | 较高 |
| 易用性 | 简单,实现接口即可 | 需要调用 API,但 API 设计直观 |
| 适用场景 | - 简单的、仅限 Java 环境内的对象传递 - RMI (远程方法调用) 等特定 Java 技术栈 - 快速原型验证 |
- 强烈推荐:Web API 开发(HTTP Body) - 微服务之间的通信 - 对象持久化到文件/数据库 - 需要跨语言交互的场景 |
- 除非有特殊原因(如使用 RMI 或遗留系统),否则强烈推荐使用 Jackson 或 Gson 等第三方库。
- 对于绝大多数现代应用,尤其是 Web 开发和微服务架构,Jackson 是事实上的标准选择,它在性能、易用性和功能上都表现优异。
- 原生序列化机制更多存在于 Java 历史代码和特定技术中,新的项目应尽量避免使用它来进行对象和字节数组的转换。
