参考文献
- Java 序列化
- 稀土掘金 - 基于 Jackson 序列化
- 测试各种 JSON 工具在多场景下的性能
- FastJson2快速入门
- 探究 JDK+Json+Hessian+Protobuf 序列性能
- Protobuf 官方手册
Java 序列化
什么是 Java 序列化
- 序列化:将Java对象转换为字节序列的过程
- 反序列化: 把字节序列恢复成 Java 对象的过程
序列化的必要性
JVM 停止后想保存对象到磁盘或传输到远程机器。但硬件只认识二进制,所以要把对象转化为字节数组,这就是序列化。
序列化的用途
- 序列化机制可以让对象保存到磁盘上,减轻内存压力的同时,起到了持久化的作用
- 序列化机制让 Java 对象可以在网络传输
序列化器的实现方式
序列化器的实现方式
主流的比如: 基于 JDK, JSON, Hessian, Kryo, protobuf
| 序列化器 | 优点 | 缺点 |
|---|---|---|
| 原生 JDK Serializable | 1. 简单易用,对简单 Java 应用内对象持久化需求便捷。 2. 兼容性好,与 Java 语言及相关框架、组件集成度高,在标准 Java 生态系统内进行对象传递、存储等操作无大兼容性问题。 | 1. 性能相对于其他序列化框架低。 2. 序列化后生成数据体积大。 3. 不支持多语言项目。 |
| JSON | 1. 易读性好,可读性强,便于人类理解和调试。 2. 跨语言支持广泛,几乎所有编程语言都有 JSON 的解析和生成库。 | 1. 序列化后的数据量相对较大,因为 JSON 使用文本格式存储数据,需要额外的字符表示键、值和数据结构。 2. 不能很好地处理复杂的数据结构和循环引用,可能导致性能下降或者序列化失败。 |
| Hessian | 1. 二进制序列化,序列化后的数据量较小,网络传输效率高。 2. 支持跨语言,适用于分布式系统中的服务调用。 | 1. 性能较 JSON 略低,因为需要将对象转换为二进制格式。 2. 对象必须实现 Serializable 接口,限制了可序列化的对象范围。 |
| Kryo | 1. 高性能,序列化和反序列化速度快。 2. 支持循环引用和自定义序列化器,适用于复杂的对象结构。 3. 无需实现 Serializable 接口,可以序列化任意对象。 | 1. 不跨语言,只适用于 Java。 2. 对象的序列化格式不够友好,不易读懂和调试。 |
| Protobuf | 1. 高效的二进制序列化,序列化后的数据量极小。 2. 跨语言支持,并且提供了多种语言的实现库。 3. 支持版本化和向前/向后兼容性。 | 1. 配置相对复杂,需要先定义数据结构的消息格式。 2. 对象的序列化格式不易读懂,不便于调试。 |
基于原生 Serializable 实现
涉及 API
java.io.ObjectOutputStream
java.io.ObjectInputStream
java.io.Serializable
java.io.Externalizable
Serializable 接口
Serializable 接口是标记接口,无方法或字段。实现它标志类的对象可序列化。
public interface Serializable {
}
Externalizable 接口
Externalizable 继承 Serializable 接口,定义了 writeExternal() 和 readExternal() 抽象方法。开发人员用 Externalizable 实现序列化和反序列化需重写这两个方法。
public interface Externalizable extends java.io.Serializable {
void writeExternal(ObjectOutput out) throws IOException;
void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}
ObjectOutputStream / ObjectInputStream
对象输出流和对象输入流
- 对象输出流的
writeObject(Object obj)方法可序列化指定对象,将字节序列写入目标输出流。 - 对象输入流的
readObject()方法从输入流读取字节序列,反序列化后返回对象。
使用
定义一个实体类实现 Serializable
import lombok.Data;
import java.io.Serializable;
@Data
public class User implements Serializable {
private String userId;
private Integer age;
private String name;
}
定义 Serializer 接口
public interface Serializer {
/**
* 序列化
*
* @param object
* @return
* @throws IOException
*/
void serialize(Object object) throws IOException;
/**
* 反序列化
*
* @param path
* @param type
* @param <T>
* @return
* @throws IOException
*/
<T> T deserialize(String path, Class<T> type) throws IOException;
}
实现基于 JDK 的序列化器
-
使用
ObjectOutputStream类的writeObject方法,实现序列化 -
使用
ObjectInputStream类的readObject方法,实现反序列化
public class JdkSerializer implements Serializer {
@Override
public void serialize(Object object) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(Files.newOutputStream(Paths.get("src/main/resources/test.out")));
objectOutputStream.writeObject(object);
objectOutputStream.flush();
objectOutputStream.close();
}
@Override
@SuppressWarnings("all")
public <T> T deserialize(String path, Class<T> type) throws IOException {
ObjectInputStream objInputStream = new ObjectInputStream(Files.newInputStream(Paths.get(path)));
try {
return (T) objInputStream.readObject();
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
测试
public class SerializeDirector {
public static void main(String[] args) {
Serializer serializer = new JdkSerializer();
User user = new User();
user.setName("Elon Mask");
user.setAge(45);
user.setUserId("11111");
try {
serializer.serialize(user);
User deserializeUser = serializer.deserialize("src/main/resources/test.out", User.class);
System.out.println(deserializeUser);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
输出
注意
1. static 静态变量和 transient 修饰的字段是不会被序列化的
静态成员变量属于类级别,因序列化针对对象,故不能序列化。
transient 关键字,它可以阻止修饰的字段被序列化到文件中,在被反序列化后,transient 字段的值被设为初始值,比如 int 型的值会被设置为 0
2. serilalVersionUID 的作用
JAVA 序列化通过判断类的 serialVersionUID 验证版本一致。反序列化时,JVM 比较传来字节流与本地实体类的 serialVersionUID,相同则反序列化成功,不同则抛出 InvalidClassException 异常。
《阿里巴巴Java开发手册》中关于serialVersionUID的使用规约
- 强制:序列化类新增属性时,请不要修改serialVersionUID字段,避免反序列失败;如果完全不兼容升级,避免反序列化混乱,那么请修改serialVersionUID值.
这样规定的原因
- 保证序列化与反序列化的兼容性
- 避免反序列化失败导致的系统错误
- 便于开发者控制版本和兼容性
3. 如果某个序列化类的成员变量是对象类型,则该对象类型的类必须实现序列化
4. 子类实现了 Serializable, 父类没有实现 Serializable 接口的话,父类不会被序列化
实现 JSON 序列化
主流的 JSON 工具有:
- Jackson
- FastJson
- Gson
- Hutool
关于以上各个工具的性能比较可以参考: 测试 JSON 工具的版本
基于 Jackson 实现
导入依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.4</version>
</dependency>
扩展实体类
@Data
public class User implements Serializable {
private String userId;
private Integer age;
private String name;
List<String> hobbies;
Map<String, Integer> parents; //家人 key 年龄 val
public static User getInstance() {
User user = new User();
user.setUserId("111");
user.setAge(22);
user.setName("Elon Mask");
user.hobbies = new ArrayList<String>() {{
add("AAA");
add("BBB");
add("CCC");
}};
user.parents = new HashMap<String, Integer>() {
{
put("ABC", 111);
put("SHUC", 123);
}
};
return user;
}
}
实现基于 Jackson 序列化器
writeValue可以接收File作为参数,将 JSON 序列化结果保存到文件中writeValueAsString将JSON序列化结果以String形式返回writerWithDefaultPrettyPrinter方法可以将JSON序列化结果进行格式化,更好的显示结构,易于查看
public class JacksonSerializer implements Serializer {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public void serialize(Object object) throws IOException {
//Object Mapper 作为 Jackson 的 API 工具类存在
objectMapper.writeValue(new File("src/main/resources/testJson.out"), object);
//将 user 对象以 JSON 格式进行序列化 String 对象
String jsonStr = objectMapper.writeValueAsString(object);
System.out.println(jsonStr);
//格式美化
String prettyJson = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
System.out.println(prettyJson);
}
//从文件中读取JSON字符串,反序列化为java对象
@Override
public <T> T deserialize(String path, Class<T> type) throws IOException {
return (T) objectMapper.readValue(new File(path), type);
}
//将JSON字符串反序列化为java对象
public User parseUser(String str) throws JsonProcessingException {
String jsonInString = "{"name":"乔丹","age":45,"hobbies":["高尔夫球","棒球"]}";
return objectMapper.readValue(jsonInString, User.class);
}
}
测试
@Test
public void testJsonSerializer() throws IOException {
String targetPath = "src/main/resources/testJson.out";
Serializer serializer = new JacksonSerializer();
serializer.serialize(User.getInstance());
User deserializedUser = serializer.deserialize(targetPath, User.class);
System.out.println(deserializedUser);
User parseUser = ((JacksonSerializer) serializer).parseUser("{"name":"乔丹","age":45,"hobbies":["高尔夫球","棒球"]}");
System.out.println(parseUser);
}
序列化结果
反序列化结果
Jackson 常用注解
注解 - @JsonProperty
@JsonProperty 可影响对象属性在序列化和反序列化时的重命名。
@Data
public class User implements Serializable {
....
@JsonProperty("User's parents")
Map<String, Integer> parents; //家人 key 年龄 val
}
注解 - @JsonInclude
不想让 null 值出现在 JSON 序列化结果中,可使用下面方法。
@JsonInclude(JsonInclude.Include.NON_NULL)
private Integer age;
忽略字段
public static User getInstance() {
User user = new User();
user.setUserId("111");
// user.setAge(22);
user.setName("Elon Mask");
}
若要在某次序列化全局范围忽略 null 成员变量,可用下面 API。
借助 API - setSerializationInclusion(JsonInclude.Include.NON_NULL)
....
private ObjectMapper objectMapper = new ObjectMapper();
{
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
}
....
测试 - 序列化后移除为 null 的成员变量
注解 - 忽略指定字段
@JsonIgnore—— 加在类成员变量上面,该成员变量将被排除在序列化和反序列化的过程之外@JsonIgnoreProperties—— 加在类声明上面,指定该类里面哪些字段被排除在序列化和反序列化的过程之外
基于 FastJson2 实现
导入依赖
<!-- 测试-多种序列化方法-->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.8</version>
</dependency>
常用类和 API
package com.alibaba.fastjson2;
class JSON {
// 将字符串解析成JSONObject
static JSONObject parseObject(String str);
// 将字符串解析成JSONArray
static JSONArray parseArray(String str);
// 将字符串解析成Java对象
static T parseObject(byte[] utf8Bytes, Class<T> objectClass);
// 将Java对象输出成字符串
static String toJSONString(Object object);
// 将Java对象输出成UT8编码的byte[]
static byte[] toJSONBytes(Object object);
}
class JSONObject {
Object get(String key);
int getIntValue(String key);
Integer getInteger(String key);
long getLongValue(String key);
Long getLong(String key);
T getObject(String key, Class<T> objectClass);
// 将JSONObject对象转换为Java对象
T toJavaObject(Class<T> objectClass);
}
class JSONReader {
// 构造基于String输入的JSONReader
static JSONReader of(String str);
// 构造基于ut8编码byte数组输入的JSONReader
static JSONReader of(byte[] utf8Bytes);
// 构造基于char[]输入的JSONReader
static JSONReader of(char[] chars);
// 构造基于json格式byte数组输入的JSONReader
static JSONReader ofJSONB(byte[] jsonbBytes)
}
测试 - 将字符串转换为对象
@Test
public void testParseJsonByFastJson() {
//转换 JSONObject
String str = "{"name":"乔丹","age":45,"hobbies":["高尔夫球","棒球"]}";
JSONObject jsonObject = JSON.parseObject(str);
System.out.println(jsonObject.toJSONString());
System.out.println("Name = " + jsonObject.getString("name"));
System.out.println("Age = " + jsonObject.getIntValue("age"));
}
输出结果
测试 - 将对象转换为 JSON 字符串
@Test
public void testObj2Json() {
User user = User.getInstance();
String jsonStr = JSON.toJSONString(user);
System.out.println(jsonStr);
}
输出结果
基于 FastJson2 实现序列化器
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
public class FastJson2Serializer implements Serializer {
@Override
public void serialize(Object object) throws IOException {
String jsonString = JSON.toJSONString(object);
String filePath = "src/main/resources/test_fastjson2.out";
// Write the JSON string to a file
Files.write(Paths.get(filePath), jsonString.getBytes());
}
@Override
public <T> T deserialize(String path, Class<T> type) throws IOException {
// Read the JSON string from the file
String jsonString = new String(Files.readAllBytes(Paths.get(path)));
// Convert JSON string back to an object of the given type
return JSON.parseObject(jsonString, type);
}
}
测试
@Test
public void testFastJson2Converter() {
FastJson2Serializer fastJson2Serializer = new FastJson2Serializer();
User result = null;
try {
fastJson2Serializer.serialize(User.getInstance());
result = fastJson2Serializer.deserialize("src/main/resources/test_fastjson2.out", User.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
System.out.println(result);
}
基于 Hessian 序列化
介绍
Hessian 是 Caucho Technology 开发的轻量级二进制序列化框架,可用于不同编程语言间高效对象序列化和反序列化,实现跨语言 RPC 或对象传输。与 JSON、XML 等文本格式序列化方式相比,Hessian 序列化后的二进制数据体积小、传输效率高,在分布式系统和网络应用中广泛应用。
基于二进制实现 Hessian 也是 Dubbo 中默认的序列化机制
导入依赖
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>4.0.66</version>
</dependency>
测试常用方法
@Test
public void testHessianConvert() throws Exception {
//序列化
FileOutputStream outputStream = new FileOutputStream("src/main/resources/testHessian.out");
Hessian2Output hop = new Hessian2Output(outputStream);
hop.writeObject(User.getInstance());
hop.flush();
hop.close();
//反序列化
FileInputStream inputStream = new FileInputStream("src/main/resources/testHessian.out");
Hessian2Input hip = new Hessian2Input(inputStream);
User user = (User) hip.readObject();
hip.close();
System.out.println(user);
}
基于 Protobuf 序列化
介绍
Protobuf 是 Google 开发的数据序列化和反序列化工具,类似 XML 和 JSON,更小巧、快速、简单,用于不同应用程序间数据交换和存储,在网络通信和分布式系统中表现出色。
Protobuf 优势
- 高效: 二进制格式序列化,数据量小,省带宽和存储资源,提传输存储效率;
- 跨语言: 支持多编程语言,方便异构系统集成通信;强类型且兼容,.proto 文件定义数据结构,减少错误风险,支持字段操作兼容,便于系统升级;
- 代码生成自动化,根据.proto 文件自动生成代码,减工作量提效率,性能高。
Protobuf VS Json
- Json 是字符串数据,转换需先转流再转字符串。与二进制序列化技术比,效率和性能有差异。
- Json 字符串以明文传输有数据泄露风险。
- Json 生成字符串数据体积较大,相比二进制序列化技术,传输有额外损耗。
美中不足: 使用 ProtoBuf 技术不像 JDK、Json 序列化便捷,很麻烦。生成的 Java 代码与传统风格不同,对原有代码侵入性高,改造成本大。
应用场景
- 分布式系统通信: 分布式系统中,Protobuf 因高效和跨语言性,是不同服务数据交互的理想选择,能快速传输数据、降低通信延迟、提升系统性能。
- 数据存储: 它可将需长期存储的数据序列化为紧凑二进制格式,节省空间且提高访问效率。
- 远程过程调用: 在 RPC 框架中,可作为数据序列化标准传递参数和返回值
- 配置文件管理: 还能用于定义和解析配置文件,方便共享和更新。
Protobuf 环境安装
参考文档
安装 ProtoBuf 编译器 - [Win 为例]
-
进入 protobuf的 github的发布地址: - protobuf 编译器发布地址
-
选取标准版比如 - v.3.x.x 我选择的是 v3.2.0
-
下载压缩包比如 Win 下载 XXX-win64.zip
-
安装完成后,将 ⌈protoc 安装目录⌋ /bin 路径添加到 PATH 环境变量
-
打开 cmd,命令窗口执行 protoc命令
确认安装版本 - protoc --version
测试 - 常用方法
导入依赖
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.21.12</version>
</dependency>
项目当前包下创建一份 .proto文件
syntax = "proto3";
// 当前 .proto 文件所在的目录
package com.jools.javase.serialize;
// .proto 文件生成 .java 文件时的目录
option java_package = "com.jools.javase.serialize.protoc";
//生成的 Java 文件名
option java_outer_classname = "ProtoUser";
//生成 equals 和 hash 方法
option java_generate_equals_and_hash = true;
//是否划分为多个文件
option java_multiple_files = false;
message User {
string name = 1;
int32 age = 2;
string email = 3;
}
安装插件 (可选)
借助 IDEA 生成
右键 .protoc 文件 - 生成可选项
quick gen protobuf here:在.proto文件所在的目录下生成.java文件;
quick gen protobuf rules:根据.proto中配置的java_package生成.java文件
点击 quick gen protobuf rules
生成了基于配置的 ProtoUser 类名的 .java 文件
基于 Protobuf 实现序列化/反序列化
将对象序列化为字节数组 / 反序列化
@Test
public void testProtoBufEasy() throws InvalidProtocolBufferException {
//通过 Builder 创建实例化对象
ProtoUser.User.Builder newUser = ProtoUser.User.newBuilder();
//实例化对象填充数据
newUser.setName("Elon Mask").setAge(45).setEmail("SpaceX001@gmail.com");
//构建实例化对象
ProtoUser.User build = newUser.build();
byte[] userBytes = build.toByteArray();
System.out.println("Protobuf压缩之后得到的长度:" + userBytes.length);
//反序列化
ProtoUser.User user = ProtoUser.User.parseFrom(userBytes);
System.out.println(user);
}
输出
序列化成字节数组 + 反序列化字节数组
import com.google.protobuf.Message;
import com.google.protobuf.Parser;
import com.jools.rpc.serializer.Serializer;
import java.io.IOException;
public class ProtoBufSerializer implements Serializer {
/**
* 序列化后,将对象转换为字节数组
*
* @param object 需要被序列化的对象
* @param <T> 对象类型
* @return 序列化后的字节数组
* @throws IOException 如果序列化失败
*/
@Override
public <T> byte[] serialize(T object) throws IOException {
if (object instanceof Message) {
return ((Message) object).toByteArray();
} else {
throw new IllegalArgumentException("Object must be an instance of com.google.protobuf.Message");
}
}
/**
* 反序列化,将字节数组转成对象实例
*
* @param bytes 字节数组
* @param type 目标类的 Class 类型
* @param <T> 目标类的类型
* @return 反序列化后的对象
* @throws IOException
*/
@Override
public <T> T deserialize(byte[] bytes, Class<T> type) throws IOException {
try {
// 检查类型是否是 Protobuf Message 的子类
if (Message.class.isAssignableFrom(type)) {
// 获取 Protobuf 的 parser 方法
// Protobuf 生成的类通常会有静态的 `parser()` 方法
Parser<?> parser = (Parser<?>) type.getMethod("parser").invoke(null);
// 解析字节数组
@SuppressWarnings("unchecked")
T message = (T) parser.parseFrom(bytes);
return message;
} else {
throw new IllegalArgumentException("Type must be a subclass of com.google.protobuf.Message");
}
} catch (Exception e) {
throw new IOException("Failed to deserialize", e);
}
}
}
ProtoBuf 内的常用数据类型
| Protobuf类型 | 对应Java类型 | 默认值 | 说明 |
|---|---|---|---|
| int32 | int整数型 | 0 | 常规的有符号32位整数类型 |
| int64 | long长整型 | 0 | 常规的有符号64位整数类型 |
| float | float浮点型 | 0 | 单精度浮点型 |
| double | double双精度浮点型 | 0 | 双精度浮点型 |
| bool | boolean布尔型 | false | 表示布尔值 |
| string | String字符串 | 空字符串 | 用于存储字符串数据 |
| bytes | ByteString类型 | 无(表示字节序列) | 对应字节序列数据 |
| sint32 | int整数型 | 0 | 有符号的整数类型,编码负数时比int32高效 |
| unit32 | int整数型 | 0 | 无符号的整数类型,编码正数时比int32高效 |
| sint64 | long长整型 | 0 | 有符号的64位长整型 |
| unit64 | long长整型 | 0 | 无符号的64位长整型 |
支持 枚举、内部类 语法
syntax = "proto3";
message User {
//字段值不可重复
uint32 id = 1;
//Field number 1 has already been used by field 'id'
// string name = 1;
string name = 2;
//使用 Hobby 统计枚举作为字段
Hobby hobby = 3;
//使用内部 Pet
Pet pet = 4;
message Pet {
string name = 5;
uint32 age = 6;
}
}
enum Hobby {
//First enum value must be 0 in proto3
FOOT_BAELL = 0;
}
支持 List \ Map 数据类型
//List
repeated Hobby hobbies = 7;
//Map
map<string, Pet> pets = 8;
基于 Kryo 实现序列化
介绍
Kryo 是快速高效的 Java 对象序列化框架,专注高性能序列化和反序列化,比 Java 标准序列化及其他常见方式在速度和数据大小方面有优势,在高性能要求的 Java 应用中广泛使用。
Kryo 优势
- 高性能
- 序列化后数据量小
- 支持复杂对象结构
基于 Kryo 序列化/反序列化
Kryo 为了提供性能和减少序列化结果的体积,提供注册方式
@Test
public void testKryoEasy() {
Kryo kryo = new Kryo();
User user = new User();
user.setName("Elon Mask");
user.setAge(45);
user.setUserId("0001");
//需要注册
kryo.register(User.class);
//可以指定 ID, 必须大于 0
// kryo.register(User.class, 1);
//序列化对象 -> 字节数组
ByteArrayOutputStream bOpt = new ByteArrayOutputStream();
Output output = new Output(bOpt);
kryo.writeObject(output, user);
output.close();
byte[] byteArray = bOpt.toByteArray();
System.out.println("Kryo 压缩之后得到的字节数组长度:" + byteArray.length);
//反序列化为对象
ByteArrayInputStream bInpt = new ByteArrayInputStream(byteArray);
Input input = new Input(bInpt);
User deserialized = kryo.readObject(input, User.class);
input.close();
System.out.println("反序列化后得到:" + deserialized);
}
输出
问题 - Kryo 线程不安全
- 内部状态修改: Kryo 在序列化和反序列化中维护内部状态,如注册类信息、对象引用缓存等。多线程同时访问和修改易致数据不一致或错误结果,如线程注册新类时另一线程序列化可能找不到正确类信息引发异常。
- 缓存机制: Kryo 用缓存提高性能,但多线程下可能同时访问修改缓存致数据不一致,如一个线程放对象入缓存另一个同时查找修改会混乱。
- 缺乏同步机制: Kryo 设计初衷为高性能,缺乏完善线程同步机制,多线程并发易有线程安全问题。
解决 - Kryo 线程不安全序列化/反序列化
方式一:基于 ThreadLocal
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class KryoSerializer implements Serializer {
/**
* Kryo 实例应该是线程不安全的,所以每个线程应该有自己的 Kryo 实例
*/
private static final ThreadLocal<Kryo> KRYO_THREAD_LOCAL = ThreadLocal.withInitial(Kryo::new);
@Override
public <T> byte[] serialize(T object) throws IOException {
Kryo kryo = KRYO_THREAD_LOCAL.get();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
Output output = new Output(byteArrayOutputStream);
kryo.writeObject(output, object);
output.close();
return byteArrayOutputStream.toByteArray();
}
@Override
public <T> T deserialize(byte[] bytes, Class<T> type) throws IOException {
Kryo kryo = KRYO_THREAD_LOCAL.get();
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
Input input = new Input(byteArrayInputStream);
T object = kryo.readObject(input, type);
input.close();
return object;
}
}
方式二: 基于池技术
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.util.Pool;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class KryoSerializer implements Serializer {
private static Pool<Kryo> kryoPool = new Pool<>(true, false, 8) {
@Override
protected Kryo create() {
return new Kryo();
}
};
@Override
public <T> byte[] serialize(T object) throws IOException {
//从池中获取对象
Kryo kryo = kryoPool.obtain();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
Output output = new Output(byteArrayOutputStream);
kryo.writeObject(output, object);
output.close();
//将 Kryo 对象归还
kryoPool.free(kryo);
return byteArrayOutputStream.toByteArray();
}
@Override
public <T> T deserialize(byte[] bytes, Class<T> type) throws IOException {
//从池中获取对象
Kryo kryo = kryoPool.obtain();
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
Input input = new Input(byteArrayInputStream);
T object = kryo.readObject(input, type);
input.close();
//将 Kryo 对象归还
kryoPool.free(kryo);
return object;
}
}