文章目录

  • 前言
  • 一、Kryo序列化(优先选择)
    • 介绍
    • 快速开始
    • 测试
  • 二、JDK原生序列化
    • 介绍
    • 快速开始
    • 测试
  • 三、Protobuf序列化
    • 介绍
    • 快速开始
    • 测试
  • 四、ProtoStuff
    • 介绍
    • 快速开始
    • 测试
  • 五、hessian
    • 介绍
    • 快速开始
    • 测试
  • 总结
  • 参考资料

前言

本节配套案例代码:Java-Learn—Github地址

所有博客文件目录索引:博客目录索引(持续更新)

一、Kryo序列化(优先选择)

介绍

kryo-Gihub仓库地址

Kryo 是一个高性能的序列化/反序列化工具,由于其变长存储特性并使用了字节码生成机制,拥有较高的运行速度和较小的字节码体积,并且Kryo 已经是一种非常成熟的序列化实现了,已经在 Twitter、Groupon、Yahoo 以及多个著名开源项目(如 Hive、Storm)中广泛的使用。

基于Java的快速高效的对象序列化框架,旨在提供快速、高效和易用的API。无论文件、数据库或网络数据Kryo都可以随时完成序列化。Kryo还可以执行自动深拷贝(克隆)、浅拷贝(克隆),这是对象到对象的直接拷贝,非对象→字节→对象的拷贝。支持互相引用,比如类A引用类B,类B引用类A,可以正确地反序列化。

说明:对于Kryo需要你进行提前进行手动注册class类,这种方式能够更够让序列化后的内容更小,序列化更快,当然你也可以不选择手动注册,那么内容会稍微大一些。

快速开始

<dependency><groupId>com.esotericsoftware.kryo</groupId><artifactId>kryo5</artifactId><version>5.3.0</version>
</dependency>

pojo类:

package com.changlu.serialize.pojo;import java.io.Serializable;/*** @ClassName Message* @Author ChangLu* @Date 6/14/2022 8:39 PM* @Description 消息类*/
public class Message implements Serializable {private int messageType;public int getMessageType() {return messageType;}public void setMessageType(int messageType) {this.messageType = messageType;}
}
package com.changlu.serialize.pojo;import java.io.Serializable;/*** @ClassName User* @Author ChangLu* @Date 6/14/2022 8:45 PM* @Description 用户类*/
public class User implements Serializable {private String name;private Integer age;public User(){}public User(String name, Integer age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}
}
package com.changlu.serialize.pojo;/*** @ClassName RPCResponse* @Author ChangLu* @Date 6/14/2022 8:39 PM* @Description RPC响应实体类*/
public class RPCResponse<T> extends Message{private T data;public T getData() {return data;}public void setData(T data) {this.data = data;}@Overridepublic String toString() {return "RPCResponse{" +"data=" + data +'}';}
}

序列化接口以及实现类:

package com.changlu.serialize;public interface Serializer {/*** 序列化** @param obj 要序列化的对象* @return 字节数组*/byte[] serialize(Object obj);/*** 反序列化** @param bytes 序列化后的字节数组* @param clazz 目标类* @param <T>   类的类型。举个例子,  {@code String.class} 的类型是 {@code Class<String>}.*              如果不知道类的类型的话,使用 {@code Class<?>}* @return 反序列化的对象*/<T> T deserialize(byte[] bytes, Class<T> clazz);
}
package com.changlu.serialize;import com.esotericsoftware.kryo.kryo5.Kryo;
import com.esotericsoftware.kryo.kryo5.io.Input;
import com.esotericsoftware.kryo.kryo5.io.Output;
import com.esotericsoftware.kryo.kryo5.objenesis.strategy.StdInstantiatorStrategy;
import com.esotericsoftware.kryo.kryo5.util.DefaultInstantiatorStrategy;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;/*** @ClassName KryoSerializer* @Author ChangLu* @Date 6/14/2022 8:33 PM* @Description Kryo序列化工具*/
public class KryoSerializer implements Serializer{//由于Kryo是线程不安全的,所以我们这里使用ThreadLocal来解决线程安全问题public static ThreadLocal<Kryo> kryoThreadLocal = ThreadLocal.withInitial(()->{Kryo kryo = new Kryo();kryo.setReferences(true);//检测循环依赖,默认值为true,避免版本变化显示设置//方式一:设置无需注册,那么之后就无需对需要进行序列号的类进行注册(性能略差)//kryo.setRegistrationRequired(false);//默认值为true,避免版本变化显示设置((DefaultInstantiatorStrategy)kryo.getInstantiatorStrategy()).setFallbackInstantiatorStrategy(new StdInstantiatorStrategy());//设置默认的实例化器//方式二:由于默认是需要进行注册的,若是不设置为false,那么就需要进行手动注册class类kryo.register(User.class);kryo.register(RPCResponse.class);return kryo;});@Overridepublic byte[] serialize(Object obj) {try (ByteArrayOutputStream baos = new ByteArrayOutputStream();final Output output = new Output(baos)) {Kryo kryo = kryoThreadLocal.get();//进行序列化kryo.writeObject(output, obj);kryoThreadLocal.remove();return output.toBytes();} catch (IOException e) {throw new RuntimeException("Serialization failed");}}@Overridepublic <T> T deserialize(byte[] bytes, Class<T> clazz) {try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);Input input = new Input(byteArrayInputStream)) {Kryo kryo = kryoThreadLocal.get();Object obj = kryo.readObject(input, clazz);kryoThreadLocal.remove();return clazz.cast(obj);} catch (IOException e) {throw new RuntimeException("Serialization failed");}}}

测试

当前Kryo采用的是注册class的方式来进行反序列化的:

package com.changlu.serialize;import com.changlu.serialize.pojo.RPCResponse;
import com.changlu.serialize.pojo.User;
import org.junit.Test;/*** @Description: 序列号测试工具* @Author: changlu* @Date: 9:31 AM*/
public class SerializerTest {@Testpublic void test(){//测试对象RPCResponse<User> rpcResponse = new RPCResponse<>();rpcResponse.setData(new User("changlu", 123));//测试kryo序列化testSerialize(new KryoSerializer(), rpcResponse);}public <T> void testSerialize(Serializer serializer, T t) {System.out.println(String.format("=====开始序列化:%s=====", serializer.getClass()));System.out.println("开始进行序列化");long startTime = System.nanoTime();//序列化byte[] data = serializer.serialize(t);long endTime = System.nanoTime();System.out.println("  序列化时间为:" + (endTime - startTime) / 1000000000.0 + "秒");System.out.println("  序列化后的内容为:" + new String(data));System.out.println("  序列化后的长度为:" + data.length);System.out.println("开始进行反序列化");startTime = System.nanoTime();//反序列化System.out.println("  反序列化后得到的对象为:" + serializer.deserialize(data, t.getClass()));endTime = System.nanoTime();System.out.println("  反序列化时间为:" + (endTime - startTime) / 1000000000.0 + "秒");System.out.println(String.format("=====结束序列化:%s=====", serializer.getClass()) + "\n");}}

二、JDK原生序列化

介绍

介绍:Java类通过实现Serializable接口来实现该类对象的序列化,这个接口非常特殊,没有任何方法,只起标识作用。Java序列化保留了对象类的元数据(如类、成员变量、继承类信息),以及对象数据等,兼容性最好,但不支持跨语言,而且性能一般。

对于serialVersionUID 的说明:序列化号 serialVersionUID 属于版本控制的作用。序列化的时候 serialVersionUID 也会被写入二级制序列,当反序列化时会检查 serialVersionUID 是否和当前类的 serialVersionUID 一致。如果 serialVersionUID 不一致则会抛出 InvalidClassException 异常。强烈推荐每个序列化类都手动指定其 serialVersionUID,如果不手动指定,那么编译器会动态生成默认的序列化号

一般不使用原生的JDK序列化:

  1. 不支持跨语言调用 : 如果调用的是其他语言开发的服务的时候就不支持了。
  2. 性能差 :相比于其他序列化框架性能更低,主要原因是序列化之后的字节数组体积较大,导致传输成本加大。

快速开始

基于目录一中的包来进行实现即可,只要实现serialize的接口,编写序列化与反序列化方法即可!

package com.changlu.serialize;import java.io.*;/*** @Description: JDK原生序列化* @Author: changlu* @Date: 9:54 AM*/
public class JdkSerializer implements Serializer{@Overridepublic byte[] serialize(Object obj) {try (ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);) {oos.writeObject(obj);return baos.toByteArray();} catch (IOException e) {throw new RuntimeException("Serialization failed");}}@Overridepublic <T> T deserialize(byte[] bytes, Class<T> clazz) {try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);ObjectInputStream ois = new ObjectInputStream(bais);) {return  (T) ois.readObject();} catch (IOException | ClassNotFoundException e) {throw new RuntimeException("Serialization failed");}}
}

测试

基于一中的测试添加一条代码即可来进行测试:

//测试JDK原生序列化工具
testSerialize(new JdkSerializer(), rpcResponse);

发现:JDK的序列化与反序列化能够比Kryo的还快,但是序列化后的大小是大了几十倍了。


三、Protobuf序列化

介绍

protobuf—Github地址、protobuf-java

介绍:Protobuf 出自于 Google,性能还比较优秀,也支持多种语言,同时还是跨平台的。就是在使用中过于繁琐,因为你需要自己定义 IDL 文件和生成对应的序列化代码。这样虽然不然灵活,但是,另一方面导致 protobuf 没有序列化漏洞的风险。

Protobuf 包含序列化格式的定义、各种语言的库以及一个 IDL 编译器。正常情况下你需要定义 proto 文件,然后使用 IDL 编译器编译成你需要的语言。

正常流程:定义proto文件 -> 使用proto编译工具编译得到Java类 -> 使用该类来进行序列化与反序列化。

快速开始

引入依赖

<!--      protobuf  -->
<dependency><groupId>com.google.protobuf</groupId><artifactId>protobuf-java</artifactId><version>3.21.1</version>
</dependency>

使用步骤

1、首先安装proto的转换工具

下载地址

安装配置好对应的path路径,测试下命令:

protoc --version

安装这个proto工具的目的是将对应自己编写的.proto文件转为一个Java类,使用这个Java类即可进行序列化与反序列化。

  • 在一个.proto文件中可以写多个结构体都是可以的。

2、编写proto文件

使用ProtoBuf序列化数据—可查看对应protobuf对应java的类型

syntax = "proto3";
option java_package = "com.changlu.serialize.protobuf";
option java_outer_classname = "DemoModel";message User {string name = 1;uint32 age = 2;
}message Message {uint32 message_type = 1;
}
  • java_package:表示目标生成的包名路径。
  • java_outer_classname:目标生成的工具类名称。

说明:若是觉得自己编写比较麻烦,那么我们可以自己先定义Java实体类,然后使用IDEA的插件将这个实体类转为对应proto的struct类型。

  • 插件名称:pojo to proto;protobuf插件安装使用

3、准备好proto文件了之后,我们就要开始生成对应的工具类了

当前路径在main/proto/xxx下,我们想要输出到对应的main/java/com/changlu/serialize/protobuf/xxx中:

接着我们输入命令:

# -I:我自己编写的.proto 文件的位置。
# --java out 输出位置会以完整包名的形式出输出,我指定的是上级目录的java中,此时配合proto文件里的com.changlu.xxx,即可输出到我想要的目录下
# ./subscribeReq.proto:当前需要编译成java的proto文件名。
protoc -I=./ --java_out=../java ./User.proto

ok,此时我们就有了这个工具类DemoModel,对应的User、Message的实体类都在这个DemoModel类中有了,对于序列化与反序列化操作也在这个DemoModel中进行。

测试

//测试ProtoBuf
testProtobufSerialzize();
public void testProtobufSerialzize(){//准备实体类DemoModel.User.Builder userBuilder = DemoModel.User.newBuilder();userBuilder.setAge(18);userBuilder.setName("changlu");DemoModel.User user = userBuilder.build();System.out.println("=====开始序列化:Protobuf=====");System.out.println("开始进行序列化");long startTime = System.nanoTime();//序列化byte[] data = user.toByteArray();long endTime = System.nanoTime();System.out.println("  序列化时间为:" + (endTime - startTime) / 1000000000.0 + "秒");System.out.println("  序列化后的内容为:" + new String(data));System.out.println("  序列化后的长度为:" + data.length);System.out.println("开始进行反序列化");startTime = System.nanoTime();//反序列化try {System.out.println("  反序列化后得到的对象为:" + DemoModel.User.parseFrom(data));} catch (InvalidProtocolBufferException e) {throw new RuntimeException("Serialization failed");}endTime = System.nanoTime();System.out.println("  反序列化时间为:" + (endTime - startTime) / 1000000000.0 + "秒");System.out.println("=====结束序列化:Protobuf=====" + "\n");
}

四、ProtoStuff

介绍

protostuff-github

由于 Protobuf 的易用性,它的哥哥 Protostuff 诞生了。

protostuff 基于 Google protobuf,但是提供了更多的功能和更简易的用法。虽然更加易用,但是不代表 ProtoStuff 性能更差。

快速开始

<!--      protobufstuff  -->
<dependency><groupId>io.protostuff</groupId><artifactId>protostuff-core</artifactId><version>1.7.4</version>
</dependency>
<dependency><groupId>io.protostuff</groupId><artifactId>protostuff-runtime</artifactId><version>1.7.4</version>
</dependency>
<!--      protobufstuff  -->

package com.changlu.serialize;import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;/*** @Description: ProtoStuffSer序列化工具* @Author: changlu* @Date: 11:33 AM*/
public class ProtoStuffSerializer implements Serializer{//DEFAULT_BUFFER_SIZE:512//每次序列化时使用缓冲区private static final LinkedBuffer BUFFER = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);public byte[] serialize(Object obj) {Class<?> clazz = obj.getClass();Schema schema = RuntimeSchema.getSchema(clazz);byte[] bytes;try {//序列化bytes = ProtostuffIOUtil.toByteArray(obj, schema, BUFFER);}finally {BUFFER.clear();}return bytes;}public <T> T deserialize(byte[] bytes, Class<T> clazz) {Schema<T> schema = RuntimeSchema.getSchema(clazz);//反序列化T obj = schema.newMessage();ProtostuffIOUtil.mergeFrom(bytes, obj, schema);return obj;}
}

测试

//测试ProtoStuff
testSerialize(new ProtoStuffSerializer(), rpcResponse);

说明:这个序列化算法的反序列化是最快的目前来看。

五、hessian

介绍

hessian 是一个轻量级的,自定义描述的二进制 RPC 协议。hessian 是一个比较老的序列化实现了,并且同样也是跨语言的。

dubbo RPC 默认启用的序列化方式是 hessian2 ,但是,Dubbo 对 hessian2 进行了修改,不过大体结构还是差不多。

快速开始

<!--      hessian  -->
<dependency><groupId>com.caucho</groupId><artifactId>hessian</artifactId><version>4.0.65</version>
</dependency>

package com.changlu.serialize;import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;/*** @Description: Hessian序列化* @Author: changlu* @Date: 12:38 PM*/
public class HessianSerializer implements Serializer{@Overridepublic byte[] serialize(Object obj) {try (ByteArrayOutputStream baos = new ByteArrayOutputStream();){HessianOutput hessianOutput = new HessianOutput(baos);//序列化hessianOutput.writeObject(obj);return baos.toByteArray();} catch (IOException e) {throw new RuntimeException("Serialization failed");}}@Overridepublic <T> T deserialize(byte[] bytes, Class<T> clazz) {try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);){HessianInput hessianInput = new HessianInput(bais);//反序列化return clazz.cast(hessianInput.readObject(clazz));} catch (IOException e) {throw new RuntimeException("Serialization failed");}}
}

测试

//测试Hession
testSerialize(new HessianSerializer(), rpcResponse);

总结

Kryo 是专门针对 Java 语言序列化方式并且性能非常好,如果你的应用是专门针对 Java 语言的话可以考虑使用,并且 Dubbo 官网的一篇文章中提到说推荐使用 Kryo 作为生产环境的序列化方式。

其他跨语言的序列化方式包含:Protobuf、 ProtoStuff、hessian如果有跨语言需求的话可以考虑使用,其他还包含Thrift,Avro 这些。

参考资料

[1] kryo:JavaGuide—Java 序列化详解、Kryo 序列化

[2] JDK原生:Java序列化——JDK序列化与Json序列化

[3] protobuf:Protobuf的生成及使用、Protobuf序列化、使用ProtoBuf序列化数据

[4] ProtoStuff:JavaGuide—Java 序列化详解

protobuf:Protobuf的生成及使用、Protobuf序列化、使用ProtoBuf序列化数据

[4] ProtoStuff:JavaGuide—Java 序列化详解

[5] Hession:JavaGuide—Java 序列化详解、hessian序列化原理分析

我是长路,感谢你的耐心阅读。如有问题请指出,我会积极采纳!
欢迎关注我的公众号【长路Java】,分享Java学习文章及相关资料
Q群:851968786 我们可以一起探讨学习
注明:转载可,需要附带上文章链接

Java序列化案例demo(包含Kryo、JDK原生、Protobuf、ProtoStuff以及hessian)相关推荐

  1. 在Dubbo中使用高效的Java序列化(Kryo和FST)

    作者:沈理 文档版权: Apache 2.0许可证 署名-禁止演绎 完善中-- TODO 生成可点击的目录 目录 序列化漫谈 启用Kryo和FST 注册被序列化类 无参构造函数和Serializabl ...

  2. kryo java_优化Java序列化– Java,XML,JSON,Kryo,POF

    kryo java 也许我很天真,但是我一直认为Java序列化肯定是将Java对象序列化为二进制形式的最快,最有效的方法. 毕竟Java是第7个主要发行版,所以这不是新技术,而且由于每个JDK似乎都比 ...

  3. 优化Java序列化– Java,XML,JSON,Kryo,POF

    也许我很天真,但是我一直认为Java序列化肯定是将Java对象序列化为二进制形式的最快,最有效的方法. 毕竟Java是第7个主要发行版,所以这不是新技术,并且由于每个JDK似乎都比上一个快,因此我错误 ...

  4. Java序列化与反序列化的深度思考

    目录 1.序列化与反序列化的作用 2.序列化协议 2.1 JDK序列化协议 2.1.1  Externalizable与Serializable的异同 2.2 Google ProtocolBuf 协 ...

  5. Java序列化的这三个坑千万要小心

    前几天看到一个2016年挺有趣的一个故障复盘,有一哥们给底层的HSF服务返回值加了一个字段,秉承着"加字段一定是安全的"这种惯性思维就直接上线了,上线后发现这个接口成功率直接跌0, ...

  6. Java 序列化的这三个坑千万要小心

    作者 | L       责编 | 欧阳姝黎 前几天看到一个2016年挺有趣的一个故障复盘,有一哥们给底层的HSF服务返回值加了一个字段,秉承着"加字段一定是安全的"这种惯性思维就 ...

  7. java序列化与反序列化(转)

    Java序列化与反序列化是什么?为什么需要序列化与反序列化?如何实现Java序列化与反序列化?本文围绕这些问题进行了探讨. 1.Java序列化与反序列化 Java序列化是指把Java对象转换为字节序列 ...

  8. java 序列化慢_java原生序列化慢在哪里?

    Java原生序列化和二进制序列化性能比较 序列化速度 package com.clq.netty.serializable; import java.io.ByteArrayOutputStream; ...

  9. java jdk 序列化_JDK 11:Java序列化的终结开始了吗?

    java jdk 序列化 在博客文章" 将Google的协议缓冲区与Java结合使用 "中,我引用了乔什·布洛赫(Josh Bloch)的第三版的有效Java ,他写道:" ...

最新文章

  1. 前端页面如何引入公用的页面header和footer
  2. dos下的edit命令使用详解
  3. Qt5 使用 #pragma 加载 lib 文件的注意事项
  4. 关于mysql数据库的备份和还原
  5. 介绍一个好用的日期倒计时工具
  6. java 设计模式 示例_Java中的访问者设计模式–示例教程
  7. 121. 买卖股票的最佳时机 golang
  8. POJ 2240 Arbitrage Bellman_ford 判读是否存在正环
  9. 计算机视觉图像去噪原理,图像去噪方法研究进展
  10. 【linux学习笔记五】帮助命令
  11. 7.卷2(进程间通信)---互斥锁和条件变量
  12. 辉煌十载!BDTC 2017 中国大数据技术大会在京盛大召开
  13. 远程连接工具rdcman
  14. tkmybatis详细教程(一篇就明白)
  15. 京东工业品,走在「全国统一大市场」之前?
  16. C语言 实验7-3-5 输出大写英文字母 (15分)
  17. 空城计课件软件测试,空城计课件公开课.ppt
  18. clustalw序列比对_COBALT:NCBI在线蛋白多序列比对(比ClustalW还强大的工具)
  19. 上海“富爸爸” 神奇投资之路
  20. Franka Emika Panda连接真实机械臂(一)

热门文章

  1. Linux 进程卡住了怎么办?
  2. “选用育留”,让AI搞定人力资源那点事
  3. C#微信公众号全攻略(1)--注册公众号
  4. python高级—— 从趟过的坑中聊聊爬虫、反爬、反反爬,附送一套高级爬虫试题...
  5. 怎么在html花一条竖线,怎样在网页中画一条竖线?
  6. Java中怎么定义字符串?Java基础
  7. 最新版千帆直播网站系统PHP完整版源码(PC+WAP在线观看视频)附安装教程
  8. 如何使用Canvas绘制闪电动画
  9. 用veency和realvnc实现在PC机操作iPhone
  10. 操作系统实验11:内存管理实验(DAY 62)