Java序列化方案
什么是序列化?
序列化是将对象的状态转换为字节流,以便在网络上传输或在磁盘上持久化存储。
常见的Java序列化方案
Java 原生序列化
Java 原生序列化通过实现 java.io.Serializable 接口,使对象可以被序列化和反序列化。这是最基本的序列化机制,由 Java 标准库提供支持。
优点
- 简单易用:只需实现 Serializable 接口,无需额外配置。
- 内置支持:Java 标准库自带支持,无需引入第三方库。
缺点
- 性能较低:序列化和反序列化速度较慢,生成的字节流较大。
- 性能差的原因:使用反射机制来读取对象的字段。
使用示例
- 定义可序列化的类
import java.io.Serializable;
public class Person implements Serializable {
private static final long serialVersionUID = 1L; // 推荐显式声明
private String name;
private int age;
// 构造方法
public Person() {}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// Getters 和 Setters
// toString
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 序列化和返序列化
// 创建对象
Person person = new Person("张三", 30);
// 将对象写入到文件中
try (FileOutputStream fileOut = new FileOutputStream("person.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut)) {
out.writeObject(person);
System.out.println("对象已序列化并保存到 person.ser 文件中");
} catch (IOException i) {
i.printStackTrace();
}
//从文件反序列化对象
try (FileInputStream fileIn = new FileInputStream("person.ser");
ObjectInputStream in = new ObjectInputStream(fileIn)) {
person = (Person) in.readObject();
System.out.println("从 person.ser 文件中反序列化得到对象:");
System.out.println(person);
} catch (IOException | ClassNotFoundException i) {
i.printStackTrace();
}
- 输出结果
对象已序列化并保存到 person.ser 文件中
从 person.ser 文件中反序列化得到对象:
Person{name='张三', age=30}
JSON 序列化
常用库
- Jackson
- Gson
- FastJSON
- JSON-B
- JSON-P
优点
- 可读性高:生成的 JSON 格式易于阅读和调试。
- 跨语言支持:JSON 是一种通用的数据交换格式,适用于不同编程语言之间的数据传输。
- 灵活性:支持自定义序列化和反序列化规则。
缺点
- 性能较低:相比二进制序列化,JSON 的解析速度较慢,生成的字节流较大。
- 类型安全性较低:需要额外处理类型转换问题。
使用示例
Jackson
- 引入依赖
<dependencies>
<!-- Jackson Databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
</dependencies>
- 使用 Jackson 进行序列化和反序列化
ObjectMapper mapper = new ObjectMapper();
Person person = new Person("Alice", 30);
// 序列化
String jsonString = mapper.writeValueAsString(person);
System.out.println(jsonString);
// 反序列化
Person deserializedPerson = mapper.readValue(jsonString, Person.class);
System.out.println(deserializedPerson.getName());
- 输出结果
{"name":"Alice","age":30}
Alice
30
- 注意事项
- 如果 Person 没有无参构造方法,在反序列化时,会报以下错误
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.example.Person` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{"name":"Alice","age":30}"; line: 1, column: 2]
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1915)
at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:414)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1360)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1424)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:352)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185)
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4825)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3772)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3740)
at org.example.Main.main(Main.java:18)
Gson
- 引入依赖
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
- 使用 Gson 进行序列化和反序列化
Gson gson = new GsonBuilder().setPrettyPrinting().create();
Person person = new Person("Alice", 30);
// 序列化
String jsonString = gson.toJson(person);
System.out.println(jsonString);
//output: {"name":"Alice","age":30}
// 反序列化
Person deserializedPerson = gson.fromJson(jsonString, Person.class);
System.out.println(deserializedPerson.getName());
- 输出结果
{
"name": "Alice",
"age": 30
}
Alice
FastJson
- 引入依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
- 使用 FastJson 进行序列化和反序列化
Person person = new Person("Alice", 30);
// 序列化
String jsonString = JSON.toJSONString(person);
System.out.println(jsonString);
// 反序列化
Person deserializedPerson = JSON.parseObject(jsonString, Person.class);
System.out.println(deserializedPerson.getName());
- 输出结果
{"age":30,"name":"Alice"}
Alice
JSON-B (JSON Binding)
- 概述:Java EE 的标准 JSON 绑定 API,用于将 Java 对象转换为 JSON。
- 优点:标准化,易于集成。
JSON-B 使用示例:
- 引入 JSON-B 依赖(以 Eclipse Yasson 实现为例)
<dependency>
<groupId>org.eclipse</groupId>
<artifactId>yasson</artifactId>
<version>2.0.3</version>
</dependency>
- 定义需要序列化的对象
public class Person implements Serializable{
@JsonbProperty("full_name")
private String name;
private int age;
}
// 构造函数
// Getter and Setter
// toString
- 使用 JSON-B 进行序列化和反序列化
// 将 Person 序列化为 json 字符串
Person person = new Person("张三", 30);
Jsonb jsonb = JsonbBuilder.create();
String jsonString = jsonb.toJson(person);
System.out.println(jsonString);
// 输出:{"full_name":"张三","age":30}
// 将 json 字符串反序列化为 Person 对象
Person deserializedPerson = jsonb.fromJson(jsonString, Person.class);
System.out.println(deserializedPerson.getName());
- 输出结果
{"age":30,"full_name":"张三"}
Deserialized Person:
Name: 张三
Age: 30
JSON-P (JSON Processing)
- 概述:用于处理 JSON 数据的标准 API,提供流式和树式的处理方式。
- 优点:灵活高效,适合处理大型 JSON 数据。
JSON-P 使用示例:
- 引入了 JSON-P 的依赖
<dependency>
<groupId>javax.json</groupId>
<artifactId>javax.json-api</artifactId>
<version>1.1.4</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.json</artifactId>
<version>1.1.4</version>
</dependency>
- 使用 JSON-P 进行序列化和反序列化
// 序列化
JsonObject jsonObject = Json.createObjectBuilder()
.add("name", "赵六")
.add("age", 35)
.build();
String jsonString = jsonObject.toString();
// 反序列化
JsonReader jsonReader = Json.createReader(new StringReader(jsonString));
JsonObject jsonObject = jsonReader.readObject();
String name = jsonObject.getString("name");
int age = jsonObject.getInt("age");
- 输出结果
Name: 赵六
Age: 35
XML 序列化
常用库
- JAXB (Java Architecture for XML Binding)
优点
- 标准化:XML 是一种标准的数据交换格式,广泛应用于企业级应用。
- 可读性高:类似于 JSON,XML 也具有良好的可读性。
缺点
- 冗长:相比 JSON,XML 的标签更冗长,导致生成的字节流更大。
- 解析复杂:XML 的解析过程相对复杂,性能较低。
使用示例
- 引入依赖
<dependencies>
<!-- JAXB API -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<!-- JAXB 实现 -->
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.3</version>
</dependency>
</dependencies>
- 定义可序列化的类
@XmlRootElement
public class Person implements Serializable {
private static final long serialVersionUID = 1L; // 推荐显式声明
private String name;
private int age;
public Person() {}
// 构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// Getters 和 Setters
// toString
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 需要在可序列化类中加上 @XmlRootElement 注解
- 需要设置无参构造方法
- 使用 JAXB 进行序列化和反序列化
JAXBContext context = JAXBContext.newInstance(Person.class);
Marshaller marshaller = context.createMarshaller();
Unmarshaller unmarshaller = context.createUnmarshaller();
Person person = new Person("Bob", 25);
StringWriter writer = new StringWriter();
// 序列化
marshaller.marshal(person, writer);
String xmlString = writer.toString();
System.out.println(xmlString);
// 反序列化
Person deserializedPerson = (Person) unmarshaller.unmarshal(new StringReader(xmlString));
System.out.println(deserializedPerson.getName());
- 输出结果
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><person><age>25</age><name>Bob</name></person>
Bob
二进制序列化
二进制序列化是一种将对象的状态转换为二进制格式的过程,以便能够在存储介质(如文件、数据库)中保存,或通过网络传输,并在需要时将其反序列化(即还原)回原始对象的过程。
优点
- 高性能:序列化和反序列化速度快,生成的字节流紧凑。
- 跨语言支持:支持多种编程语言,适用于分布式系统。
- 类型安全性高:通过定义明确的协议或模式,确保数据的类型安全。
缺点
- 复杂性:相比 JSON 和 XML,二进制序列化框架通常需要更多的配置和学习成本。
- 可读性低:生成的二进制数据不可读,不便于调试。
常用框架
Kryo
Kryo 介绍
Kryo 是由 Esoteric Software 开发并维护的开源序列化框架,旨在提供比 Java 内置序列化机制更高效的性能。它广泛应用于需要高效数据传输和存储的场景,如分布式系统、网络通信和大数据处理等。
主要特点
- 高性能:Kryo 的序列化和反序列化速度远超 Java 的内置序列化机制,适合对性能要求较高的应用。
- 紧凑的序列化格式:生成的二进制数据较小,有助于减少网络传输和存储空间的开销。
- 支持多种数据类型:不仅支持基本数据类型,还支持复杂的自定义对象、集合、枚举、日期等。
- 对象引用处理:能够处理对象图中的循环引用,避免重复序列化相同对象。
- 可扩展性:允许开发者自定义序列化器,以满足特定类型或复杂对象的序列化需求。
- 跨平台支持:虽然 Kryo 主要用于 Java,但也有其他语言的实现或兼容方案,便于在多语言环境中使用。
Kryo 使用示例
- 在 maven 中添加 Kryo 依赖
<dependencies>
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>5.3.0</version>
</dependency>
</dependencies>
- 定义可序列化的类
public class Person implements Serializable {
private static final long serialVersionUID = 1L; // 推荐显式声明
private String name;
private int age;
public Person() {}
// 构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// Getters 和 Setters
// toString
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 需要设置无参构造方法
- 使用 Kryo 进行序列化和反序列化
// 创建 Kryo 实例
Kryo kryo = new Kryo();
// 注册类
kryo.register(Person.class);
// 创建一个 Person 对象
Person person = new Person("张三", 30);
System.out.println("原始对象: " + person);
// 序列化对象到字节数组
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Output output = new Output(baos);
kryo.writeObject(output, person);
output.close();
byte[] bytes = baos.toByteArray();
System.out.println("序列化后的字节数组长度: " + bytes.length);
// 反序列化字节数组回对象
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
Input inputStream = new Input(bais);
Person deserializedPerson = kryo.readObject(inputStream, Person.class);
inputStream.close();
System.out.println("反序列化后的对象: " + deserializedPerson);
- 输出结果
原始对象: Person{name='张三', age=30}
序列化后的字节数组长度: 8
反序列化后的对象: Person{name='张三', age=30}
Protobuf
Protobuf 介绍
Protocol Buffers(简称 Protobuf)是由 Google 开发的一种高效、语言中立、平台中立、可扩展的结构化数据序列化机制。它主要用于在不同的系统之间交换数据,尤其适用于分布式系统和微服务架构。Protobuf 通过定义数据结构的“消息格式”来实现数据的序列化和反序列化,比传统的文本格式(如 JSON 或 XML)在速度和数据大小上具有显著优势。
主要特性
-
高效的二进制格式:Protobuf 使用紧凑的二进制格式进行数据编码,能够显著减少数据传输的大小,并提高解析速度。
-
跨语言支持:Protobuf 支持多种编程语言,包括但不限于 C++, Java, Python, Go, C#, Ruby 等,方便在不同技术栈的系统之间进行数据交换。
-
明确的接口定义:通过 .proto 文件定义数据结构,确保各方对数据格式的理解一致,减少接口不兼容的问题。
-
向前和向后兼容:Protobuf 设计允许在不破坏现有系统的情况下,向消息中添加新的字段,增强系统的可扩展性。
-
自动代码生成:使用 protoc 编译器,可以根据 .proto 文件自动生成各语言的类和方法,简化开发流程。
Protobuf 使用示例
-
安装 Protocol Buffers 编译器
- 前往 Protocol Buffers Releases 页面(https://github.com/protocolbuffers/protobuf/releases),根据操作系统下载对应版本的
protoc压缩包,例如:protoc-29.0-rc-1-win64.zip。解压下载的压缩包,并将 bin 目录添加到系统的环境变量 PATH 中,以便在终端中全局调用。
- 前往 Protocol Buffers Releases 页面(https://github.com/protocolbuffers/protobuf/releases),根据操作系统下载对应版本的
-
在 IDEA 安装 Proto Buffer 插件
![image]()
-
定义
person.proto文件,用于定义数据结构。
syntax = "proto3";
option java_package = "org.example.protobuf";
option java_outer_classname = "PersonProto";
message Person {
int32 id = 1;
string name = 2;
string email = 3;
}
- 生成 Java 类
protoc --java_out=./src/main/java person.proto
- 命令会在
org.example.protobuf包下生成PersonProto.java文件
- 在 maven 中添加 Protobuf 依赖
<dependencies>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>4.29.0-RC1</version>
</dependency>
</dependencies>
- 使用 Protobuf 进行序列化和反序列化
// 创建一个 Person 对象
Person person = Person.newBuilder()
.setId(1)
.setName("张三")
.setEmail("zhangsan@example.com")
.build();
// 序列化到文件
try (FileOutputStream output = new FileOutputStream("person.ser")) {
person.writeTo(output);
System.out.println("序列化成功,数据写入 person.ser 文件");
} catch (IOException e) {
e.printStackTrace();
}
// 从文件反序列化
try (FileInputStream input = new FileInputStream("person.ser")) {
Person deserializedPerson = Person.parseFrom(input);
System.out.println("反序列化成功:");
System.out.println("ID: " + deserializedPerson.getId());
System.out.println("Name: " + deserializedPerson.getName());
System.out.println("Email: " + deserializedPerson.getEmail());
} catch (IOException e) {
e.printStackTrace();
}
- 输出结果
序列化成功,数据写入 person.ser 文件
反序列化成功:
ID: 1
Name: 张三
Email: zhangsan@example.com
Hessian
Hessian 介绍
Apache Hessian 是一个轻量级的二进制Web服务协议,旨在简化不同编程语言之间的远程通信。由Caucho Technology开发并捐赠给Apache软件基金会,Hessian 提供了一种高效、简洁的方式来序列化和反序列化数据,使得客户端和服务器之间的数据交换更加快速和可靠。
主要特点
-
二进制协议:与基于文本的协议(如XML-RPC或JSON)相比,Hessian 使用二进制格式进行数据传输,减少了数据包的大小,提高了传输效率。
-
跨语言支持:Hessian 提供多种编程语言的实现,包括Java、C#、Python、Ruby等,使得不同技术栈的系统能够轻松集成和通信。
-
简单易用:Hessian 的接口设计简洁,易于理解和使用。开发者无需复杂的配置即可快速上手。
-
高性能:由于其轻量级和高效的序列化机制,Hessian 在处理大量数据和高并发请求时表现出色。
-
支持复杂数据类型:Hessian 能够处理复杂的数据结构,如对象、列表、映射等,满足各种应用场景的需求。
Hessian 使用示例
- 在 maven 中添加 Hessian 依赖
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>4.0.66</version>
</dependency>
- 使用 Hessian 进行序列化和反序列化
Person person = new Person("Alice", 30);
try {
// 序列化到数组
ByteArrayOutputStream bos = new ByteArrayOutputStream();
HessianOutput hessianOutput = new HessianOutput(bos);
hessianOutput.writeObject(person);
byte[] serializedBytes = bos.toByteArray();
// 反序列化字节数组回对象
ByteArrayInputStream bais = new ByteArrayInputStream(serializedBytes);
HessianInput hessianInput = new HessianInput(bais);
Person deserializedPerson = (Person) hessianInput.readObject();
} catch (IOException e) {
e.printStackTrace();
}
- 输出结果
反序列化成功:
Name: Alice
Age: 30
- 如果报以下错误,则通常是由于 Java 模块系统(自 Java 9 引入)对反射访问的限制导致,需要在 JVM 启动参数,允许 Hessian 进行反射访问:
--add-opens java.base/java.lang=ALL-UNNAMED
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make private java.lang.StackTraceElement() accessible: module java.base does not "opens java.lang" to unnamed module @722c41f4
Thrift
Thrift 介绍
Apache Thrift 是一个高效、可扩展的跨语言服务开发框架,最初由Facebook开发,现已成为Apache软件基金会的顶级项目。Thrift的设计目标是简化不同编程语言之间的通信和数据交换,使开发者能够轻松构建分布式系统和微服务架构。
主要特点
- 接口定义语言(IDL):Thrift 提供了一种简洁的IDL,用于定义服务接口和数据结构。通过编写 .thrift 文件,开发者可以描述服务的功能和数据类型。IDL 文件可以自动生成多种编程语言的代码,包括 Java、C++、Python、Go、Ruby、PHP 等,极大地减少了手动编写重复代码的工作量。
- 跨语言支持:支持多种编程语言,使得不同语言编写的服务能够无缝协作。这对于采用多语言技术栈的项目尤为重要。例如,可以用Java编写服务端,用Python编写客户端,确保两者之间的通信顺畅。
- 高效的二进制协议:Thrift 提供多种传输协议,如二进制协议(Binary Protocol)、JSON协议等。二进制协议因其高效性和紧凑性,常用于对性能要求较高的场景。支持多种传输层,如基于TCP的传输、基于HTTP的传输等,提供灵活的通信方式。
- 可插拔的传输层和协议:开发者可以根据具体需求选择或定制传输层和协议,提升系统的灵活性和可扩展性。例如,可以在同一个服务中同时支持二进制和JSON协议,以适应不同的客户端需求。
Thrift 使用示例
安装 Apache Thrift
- 从 Apache Thrift 官方网站 (https://thrift.apache.org/download) 下载适合你操作系统的版本,并按照安装说明进行安装。
定义 Thrift IDL 文件
namespace java com.example.thrift
struct User {
1: i32 id,
2: string name,
3: string email
}
使用 Thrift 编译器生成 Java 代码
thrift --gen java example.thrift
在maven 中引入 thrift 依赖
<dependencies>
<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.21.0</version>
</dependency>
</dependencies>
使用 thrift 进行序列化和反序列化
// 创建 User 对象
User user = new User();
user.setId(1);
user.setName("李四");
user.setEmail("lisi@example.com");
// 序列化
TSerializer serializer = new TSerializer(new TBinaryProtocol.Factory());
byte[] serializedData = serializer.serialize(user);
System.out.println("Serialized data: " + java.util.Arrays.toString(serializedData));
// 反序列化
User deserializedUser = new User();
TDeserializer deserializer = new TDeserializer(new TBinaryProtocol.Factory());
deserializer.deserialize(deserializedUser, serializedData);
System.out.println("Deserialized User:");
System.out.println("ID: " + deserializedUser.getId());
System.out.println("Name: " + deserializedUser.getName());
System.out.println("Email: " + deserializedUser.getEmail());


浙公网安备 33010602011771号