这里是weihubeats,觉得文章不错可以关注公众号小奏技术,文章首发。拒绝营销号,拒绝标题党
首先我们还是简单的聊聊为什么需要序列化这种东西。其实在一些网络通信中,我们知道网络传输的数据必须是二进制数据,但是在Java中我们方法的入参和出参都是对象,对象又不能直接在网络中进行传输。所以便有了序列化这玩意,将对象序列化为二进制数据然后在网络中进行传输,但是序列化和一般的其他算法不同,他需要是可逆的,因为我们网络传输完,接受到的请求的一段还需要将数据反序列化为对象进行处理。
这个过程我们用图表示如下:

总的来说就是网络上只能传输二进制文件,而我们程序需要处理的又是对象。所以就有了序列化和反序列化。只要是网络传输一般都是会涉及到序列化。
总得来说比较官方的定义就是:
没有最好的协议,只有最合适的。选择序列化协议我们需要从如下几个标准去评估
所以性能和可读性很多时候是二者不可兼得
原生的jdk序列化方式很简单,只需要实现Serializable接口即可。
我们使用简单的例子来看看
@Data
public class Student implements Serializable {
private String name;
private Integer age;
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public static void main(String[] args) throws Exception {
// 写入文件 序列化
String home = System.getProperty("user.home");
String basePath = home + "/Desktop";
FileOutputStream fos = new FileOutputStream(basePath + "student.dat");
Student student = new Student("小奏技术", 18);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(student);
oos.flush();
oos.close();
// 读取文件 反序列化
FileInputStream fis = new FileInputStream(basePath + "student.dat");
ObjectInputStream ois = new ObjectInputStream(fis);
Student readStudent = (Student) ois.readObject();
System.out.println(readStudent.toString());
ois.close();
fis.close();
}
}

非常简单
json是我们最熟悉也是最常用的一种序列化方式,他以Key-Value方式存储,没有数据类型,是一种文本序列化框架。
目前网络上主流的JSON序列化框架大致有如下几种
使用也非常简单,如果是fastjson
Student student = new Student("小奏技术", 18);
String jsonString = JSON.toJSONString(student);
Student student1 = JSON.parseObject(jsonString, Student.class);
性能不错是指一般够用,如果需要较高性能还是需要选用其他序列化方式
Protobuf是Google提出的一种数据交换的格式,是一套类似JSON或者XML的数据传输格式和规范,用于不同应用或进程之间进行通信,支持主流的编程语言如 Java、Python、C++、Go 等。
Protobuf使用的时候需要定义 IDL(Interface description language),然后使用不同语言的 IDL编译器,生成序列化工具类
<properties>
<protobuf.version>3.13.0protobuf.version>
properties>
<dependency>
<groupId>com.google.protobufgroupId>
<artifactId>protobuf-javaartifactId>
<version>${protobuf.version}version>
dependency>
<plugins>
<plugin>
<groupId>org.xolstice.maven.pluginsgroupId>
<artifactId>protobuf-maven-pluginartifactId>
<version>0.6.1version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}protocArtifact>
<pluginId>grpc-javapluginId>
<protoSourceRoot>src/main/resources/protoprotoSourceRoot>
configuration>
<executions>
<execution>
<goals>
<goal>compilegoal>
<goal>compile-customgoal>
goals>
execution>
executions>
plugin>
plugins>

syntax = "proto3"; // 指定使用 proto3 语法
option java_package = "com.xiaozou"; // 指定包名
option java_outer_classname = "StudentProtobuf"; // 指定类名
message StudentMsg {
// 类型名称 = 编号
int32 age = 1;
string name = 2;
}
message Result {
}
这里直接运行protobuf 插件

生成的代码在target目录下

我们简单看看内容

@Test
public void testProtobuf() throws Exception{
StudentProtobuf.StudentMsg.Builder builder = StudentProtobuf.StudentMsg.newBuilder();
builder.setAge(12);
builder.setName("小奏技术");
StudentProtobuf.StudentMsg studentMsg = builder.build();
// 序列化为 byte数组
byte[] bytes = studentMsg.toByteArray();
System.out.println("-----Student Byte start -------");
for (byte b : bytes) {
System.out.print(b);
}
System.out.println();
System.out.println("-----Student Byte end -------");
// 将 bytes 数组序列化为 java 对象
StudentProtobuf.StudentMsg p2 = StudentProtobuf.StudentMsg.parseFrom(bytes);
System.out.println("student age:" + p2.getAge());
System.out.println("student name:" + p2.getName());
}

Thrift是Facebook开源提供的一个高性能,轻量级RPC服务框架,其产生正是为了满足当前大数据量、分布式、跨语言、跨平台数据通讯的需求。 但是,Thrift并不仅仅是序列化协议,而是一个RPC框架。相对于JSON和XML而言,Thrift在空间开销和解析性能上有了比较大的提升,对于对性能要求比较高的分布式系统,它是一个优秀的RPC解决方案;但是由于Thrift的序列化被嵌入到Thrift框架里面,Thrift框架本身并没有透出序列化和反序列化接口,这导致其很难和其他传输层协议共同使用(例如HTTP)。
目前Thrift已经捐赠给了apache
由于以上特性导致Thrift在其他框架或者场景中应用并不算多,所以之类不作过多介绍,仅了解
Hessian 是动态类型、二进制、紧凑的,并且可跨语言移植的一种序列化框架。性能比json、xml还是要高出不少的
引入依赖
<dependency>
<groupId>com.cauchogroupId>
<artifactId>hessianartifactId>
<version>4.0.65version>
dependency>
@Test
public void testHessian() throws Exception {
Student student = new Student("小奏技术", 18);
//student对象转化为byte数组
ByteArrayOutputStream bos = new ByteArrayOutputStream();
Hessian2Output output = new Hessian2Output(bos);
output.writeObject(student);
output.flushBuffer();
byte[] data = bos.toByteArray();
bos.close();
// 将byte数组转化为student对象
ByteArrayInputStream bis = new ByteArrayInputStream(data);
Hessian2Input input = new Hessian2Input(bis);
Student deStudent = (Student) input.readObject();
input.close();
System.out.println(deStudent);
}
需要注意序列化的对象必须实现Serializable 接口
总的来说目前市面上开源的序列化框架还是比较多的,但是目前主流的还是如下两种
总的来说没有最好的序列化方式,只有最适合的。如果我们要选用哪种序列化方式可以从如下业务需求去考虑