大家好,我是程序员牛肉。
开局先问你们一个问题:你们认为什么是序列化和反序列化 ?
老实讲以前我就以为序列化就是把数据转为0101010101发送出去,反序列化就是把这些01010101再变为原数据。
如果你也是这么想的,我只能说对了,但是没完全对。如此粗暴的转换成为0101,可能会面临以下两个问题:
1.一长串01010101本质上是无法反序列化的。不要想当然的以为反序列化的时候可以直接转译。假设一个对象只是被序列化为了010101010,那么哪些01代表这个对象的类型,哪些01代表这个对象的字段?
2.同一个字段可能在不同的平台的占用字节数不同。比如说C语言在32位编辑器下,int占据4个字节,64位编译器下int占据8个字节。这种情况下你敢直接用64位编译器的环境去反序列化32位编译器系统的代码吗?
[Java不会存在第二种情况,因为java是运行在JVM上的。但与之对应的是java虽然是一次编译四处运行,但JVM是每个平台都有自己的版本。]
这两个说白了其实是一个问题:如何使得反序列化方按照指定格式进行反序列化?
目前我们给出的解决方案是;“推出中间格式”。
[其实所有的序列化框架,本质上就是在尝试设计一种序列化协议,尝试将一个对象的各种信息以一种固定的格式(序列化协议)写到二进制字节流中完成序列化,再按照这种固定的格式(序列化协议)尝试进行反序列化。]
我们以几个Java中比较常见的序列化方案举例,看一看这些序列化方案的中间格式都是什么:
JDK原生序列化方案:
原生的JDK序列化是基于内部的ObjectOutputStream完成的,反序列化是由ObejctInputStream完成的。
而这种JDK原生的序列化框架是按照下图这种序列化协议进行序列化和反序列化的。
需要的是如果你想要使用这种原生的序列化,就需要给目标类加上下面这个接口来标识当前类可以被序列化。
class Item implements Serializable
面试的时候也比较爱问一个点:“serialVersionUID”是干什么用的?
这玩意就是每个类的“身份证”。每一个待序列化的类都有一个独一无二的serialVersionUID。
当反序列化的时候,也是根据这个身份证寻找合适的类接收反序列化的数据。这个ID一般是自动生成,但是我们在开发中一般都选择手动给当前类一个serialVersionUID。
因为如果我们对序列化后的类进行了修改,那么反序列化的数据就没办法再序列化回来了,因为两个类的serialVersionUID不一致。
一般我们都是这样使用这个serialVersionUID:
public class Person implements Serializable {
private String name;
private int age;
private static final long serialVersionUID = 112312312312312L; // 定义serialVersionUID
}
JSON序列化方案:
在日常开发中,我们使用这种序列化方案也是最多的。Java也有很成熟的序列化和反序列化Json的包:JsonUtils。
一个Json格式的java类长这样:
{
"name": "程序员牛肉",
"enName": "chengxuyuanNiurou",
"age": 9,
"male": true
}
这个对应的java类就是:
public class Person {
private String name;
private String enName;
private int age;
private boolean male;
}
其实通过上述的例子,我们可以很清楚的看到Json的一个缺点:Json在传递类的时候,更加倾向于传递数据本身而会忽略数据的类型。
坏就坏在Java是强类型语言,因此在反序列化Json的时候需要通过反射来解决很多问题。而反射实在是太耗费性能了!
并且相比较于其他的序列化协议,Json也是需要占用空间比较大的一种序列化协议。
基于以上两点,使用json作为序列化协议的时候,需要序列化方和反序列化之间传递的数据比较小一点。
Hessian:
Hessian 是一种高效的二进制序列化协议,主要用于在不同语言的系统之间传输对象数据。它由 Caucho Technology 开发,最初用于 Java 应用程序之间的通信,但由于其跨语言的特性,也被应用于其他编程语言中。
基于这个序列化协议,数据被以二进制的形式进行传输,这使得它比文本类型的Json性能高出了一个档次。
可以尝试在pom.xml中引入以下文件来使用Hessian:
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>x.x.x</version>
</dependency>
他和JDK原生的序列化方案比较相似一点,也需要手动的在参数及返回值需实现 Serializable 接口。
需要注意的是Hessian本质上不是一种数据格式,而是一个轻量级的RPC服务,是基于Binary-RPC协议实现的。
如果你了解Dubbo的话,就会对这个协议比较熟悉一点。因为在Dubbo3之前,Hessian是内嵌在Dubbo中的。
关于这个协议更多的知识点可以参考Dubbo的文档:
https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/reference-manual/protocol/others/hessian/ 相关文档
protobuf:
Protobuf(Protocol Buffers)是一种由Google开发的灵活、高效的结构化数据序列化方法。它类似于XML,但是更小、更快、更简单。Protobuf是一种语言无关、平台无关的序列化框架,用于序列化结构化数据,使得数据可以被语言无关的方式进行存储和交换。
syntax = "proto3"; // 指定Protobuf语法版本
package tutorial; // 指定包名
// 定义一个Person消息
message Person {
string name = 1; // 姓名,字段编号为1
int32 id = 2; // 唯一标识符,字段编号为2
string email = 3; // 电子邮件地址,字段编号为3
// 可选的字段,如果未设置,则使用默认值
optional string phone_number = 4; // 电话号码,字段编号为4
// 枚举类型,表示一个人的性别
enum Gender {
MALE = 0; // 男性
FEMALE = 1; // 女性
OTHER = 2; // 其他
}
// 性别字段,使用上面定义的枚举类型
Gender gender = 5; // 性别,字段编号为5
}
如果你是使用go语言进行开发的开发者的话,就会对这个序列化协议很熟悉,因为go的序列化底层是基于Protobuf做的。
而Protobuf的性能也是爆杀Json了。不过Protobuf的可读性会略低于Json。
今天关于序列化的介绍就到这里了,相信你已经大致了解序列化是什么东西了。希望我的文章可以帮到你。
对于序列化你还有什么详聊的嘛?欢迎在评论区留言。
关注我,带你了解更多技术干货。