序列化(编码)是将对象序列化为二进制形式(字节数组/有序字节流),主要用于网络传输、数据持久化等,核心作用是对象状态的保存与重建。
反序列化(解码)则是将从网络、磁盘等读取的字节数组/对象字节流还原成原始对象,主要用于网络传输对象的解码,以便完成远程调用。
为什么要序列化:
JVM是Java程序运行的环境,但是同时是一个操作系统的一个应用程序,即一个进程。运行依赖于内存,因此Java中对象都是存储在内存中,准确地说是JVM的堆或栈内存中,可以各个线程之间进行对象传输,但是无法在进程之间进行传输。如果涉及到跨内存的数据传输(比如两台机器的传输),直接把对象作为参数传递就不可取了,这时就需要通过“网络”将数据传输。
序列化只是定义了拆解对象的具体规则,那这种规则肯定也是多种多样的,常见的序列化方式有:
JDK 原生
JDK自带的序列化方式,使用起来非常方便,只需要序列化的类实现了Serializable接口即可,Serializable接口没有定义任何方法和属性,所以只是起到了标识的作用,表示这个类是可以被序列化的。
把一个Java对象变为byte[]数组,需要使用ObjectOutputStream。它负责把一个Java对象写入一个字节流,如果没有实现Serializable接口而进行序列化操作就会抛出NotSerializableException异常
在反序列化时,JVM需要知道所属的class文件,在序列化的时候JVM会记录class文件的版本号,也即serialVersionUID这一变量。该变量默认是由JVM自动生成,也可以手动定义。反序列化时JVM会按版本号找指定版本的class文件进行反序列化,如果class文件有版本号在序列化和反序列化时不一致就会导致反序列化失败,会抛异常提示版本号不一致,
JDK序列化会把对象类的描述和所有属性的元数据都序列化为字节流,另外继承的元数据也会序列化,所以导致序列化的元素较多且字节流很大,但是由于序列化了所有信息所以相对而言更可靠。但是如果只需要序列化属性的值时就比较浪费。
而且因为Java的序列化机制可以导致一个实例能直接从byte[]数组创建,而不经过构造方法,因此,它存在一定的安全隐患。一个精心构造的byte[]数组被反序列化后可以执行特定的Java代码,从而导致严重的安全漏洞。
Java默认提供的序列化:无法跨语言、序列化后的码流太大、序列化的性能差
XML
优点:人机可读性好,可指定元素或特性的名称。
缺点:序列化数据只包含数据本身以及类的结构,不包括类型标识和程序集信息;只能序列化公共属性和字段;不能序列化方法;文件庞大,文件格式复杂,传输占带宽。
适用场景:当做配置文件存储数据,实时数据转换。
JSON(跨语言)
一种轻量级的数据交换格式,
优点:兼容性高、数据格式比较简单,易于读写、序列化后数据较小,可扩展性好,兼容性好、与XML相比,其协议比较简单,解析速度比较快。
缺点:数据的描述性比XML差、不适合性能要求为ms级别的情况、额外空间开销比较大。
适用场景(可替代XML):跨防火墙访问、可调式性要求高、基于Web browser的Ajax请求、传输数据量相对小,实时性要求相对低(例如秒级别)的服务。
JSON解析库:Jackson
Jackson 是当前使用最广泛的序列化和反序列化 Json的 Java 的开源框架。Spring MVC 的默认 Json 解析器便是 Jackson。 解析大的 Json 文件速度比较快; 运行时占用内存比较低,性能比较好;Jackson 有灵活的 API,可以很容易进行扩展和定制。
Jackson提供了一套用于Java(和JVM平台)的数据处理工具,包括旗舰级流JSON解析器/生成器库,匹配的数据绑定库(与JSON之间的POJO)和附加的数据格式模块。支持广泛使用的数据类型的数据类型。
三大模块:
Streaming流处理模块(jackson-core):定义底层处理流的API:JsonPaser和JsonGenerator等,并包含「特定于json」的实现。
Annotations标准注解模块(jackson-annotations):包含标准的Jackson注解
Databind数据绑定模块(jackson-databind):在streaming包上实现数据绑定(和对象序列化)支持;「它依赖于上面的两个模块」,也是Jackson的高层API(如ObjectMapper)所在的模块
Jackson-core Github地址:https://github.com/FasterXML/jackson-core
Jackson-core 官方文档:https://github.com/FasterXML/jackson-core/wiki
Jackson-annotations Github地址:https://github.com/FasterXML/jackson-annotations
Jackson-annotations 官方文档:https://github.com/FasterXML/jackson-annotations/wiki
Jackson-databind Github地址:https://github.com/FasterXML/jackson-databind
Jackson-databind 官方文档:https://github.com/FasterXML/jackson-databind/wiki
官网:http://fasterxml.com/
JSON解析库:Gson
Google Gson是一个简单的基于Java的库,用于将Java对象序列化为JSON
Github地址:https://github.com/google/gson
官方文档:https://github.com/google/gson/blob/master/UserGuide.md
API文档:https://www.javadoc.io/doc/com.google.code.gson/gson
JSON解析库:Fastjson(安全漏洞较多)
Fastjson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将Java Bean序列化为JSON字符串,也可以从JSON字符串反序列化到JavaBean。
Fastjson1 Github地址:https://github.com/alibaba/fastjson
Fastjson1 官方文档:https://github.com/alibaba/fastjson/wiki/
Fastjson2 Github地址:https://github.com/alibaba/fastjson2
Fastjson2 官方文档:https://github.com/alibaba/fastjson2/wiki
Kryo
快速序列化/反序列化工具,依赖于字节码生成机制(底层使用了 ASM 库),在序列化速度上有一定的优势,正因如此也只能限制在基于 JVM 的语言上。
Kryo支持自动深/浅拷贝,直接通过对象->对象的深度拷贝,而不是对象->字节->对象的过程。
Kryo 序列化出的结果是其自定义的、独有的一种格式。由于其序列化出的结果是二进制的,也即 byte[],因此像 Redis 这样可以存储二进制数据的存储引擎是可以直接将 Kryo 序列化出来的数据存进去。
Github地址:https://github.com/EsotericSoftware/kryo
官方文档:https://github.com/EsotericSoftware/kryo/wiki
社区:https://groups.google.com/g/kryo-users
ProtoBuf(跨语言)
谷歌开发的一款无关平台,无关语言,可扩展,轻量级高效的序列化结构的数据格式,用于将自定义数据结构序列化成字节流,和将字节流反序列化为数据结构。
适合做数据存储和为不同语言,不同应用之间互相通信的数据交换格式,只要实现相同的协议格式,后缀为proto文件被编译成不同的语言版本,这样不同的语言可以解析其它语言通过Protobuf序列化的数据。
优点:
1.跨语言:可以在多种语言之间交换结构化数据。
2.向后兼容,新增的字段不影响协议使用。
3.自动化生成代码,简单易用。
4.二进制消息,效率高,性能好。(PS:解析速度快、占用空间少)
5.Netty等框架集成了该协议,提高开发效率。
6.安全,只写入了字段号信息,被编码成二进制,破解难度大。
缺点:
1.二进制消息,可读性差。
2.字段冗余,类会越来越大,维护成本高。
Github地址:https://github.com/protocolbuffers/protobuf
官网:https://developers.google.com/protocol-buffers/
官方文档:https://developers.google.com/protocol-buffers/docs/overview
API文档:https://developers.google.com/protocol-buffers/docs/reference/overview
Thrift(跨语言)
高效的、支持多种语言的远程服务调用框架,由于Thrift提供了多语言之间的RPC服务,所以很多时候被用于序列化中。
优点:
1.序列化和RPC支持一站式解决,比ProtoBuf更方便 。
2.丰富的数据类型、对于数据字段的增删具有较强的兼容性、支持二进制压缩编码。
3.跨语言,IDL接口定义语言,自动生成多语言文件 。
省流量,体积较小 。
4.包含完整的客户端/服务端堆栈,可快速实现RPC 。
5.为服务端提供了多种工作模式,如线程池模型、非阻塞模型。
缺点:
1.不支持双通道 。
2.rpc方法非线程安全,服务器容易被挂死,需要串行化。
3.默认不具备动态特性(可以通过动态定义生成消息类型或者动态编译支持)
适用场景:分布式系统的RPC解决方案
Github地址:https://github.com/apache/thrift
官网:https://thrift.apache.org/
官方文档:hhttps://thrift.apache.org/docs/
Hessian(跨语言)
Hessian与Protobuf、Thrift一样,支持跨语言RPC通信。Hessian相比其它跨语言PRC框架的一个主要优势在于,它不是采用IDL来定义数据和服务,而是通过自描述来完成服务的定义。
Hessian 是一种动态类型、二进制序列化和 Web 服务协议,专为面向对象的传输而设计。和JDK自带的序列化方式类似,Hessian采用的也是二进制协议,Hessian序列化之后,字节数更小,性能更优。
Github地址:https://github.com/ebourg/hessian
官网:http://hessian.caucho.com/
官方文档:http://hessian.caucho.com/doc/
Avro(跨语言)
Avro是Apache Hadoop下的一个数据序列化框架。用于支持数据密集型应用,很适合远程或本地大规模数据交换和存储,解决了JSON的冗长和没有IDL的问题。
优点:支持丰富的数据类型、简单的动态语言结合功能、具有自我描述属性、提高了数据解析速度、快速可压缩的二进制数据形式、可以实现远程过程调用RPC、支持跨编程语言实现。
缺点:对于习惯于静态类型语言的用户不直观。
适用场景:在Hadoop中做Hive、Pig和MapReduce的持久化数据格式。
Github地址:https://github.com/apache/avro
官网:https://avro.apache.org/
官方文档:https://avro.apache.org/docs/current/
MessagePack(跨语言)
一个高效的二进制序列化框架,支持不同语言间的数据交换,性能更快,序列化之后的码流也更小。
优点:
1.跨语言,多语言支持。
2.序列化反序列化效率高,文件体积小,比Json小一倍。
3.兼容json数据格式
缺点:
1.缺乏复杂模型支持。msgpack对复杂的数据类型(List、Map)支持的不够,序列化没有问题,但是反序列化回来就很麻烦,尤其是对于java开发人员。
2.维护成本较高。msgpack通过value的顺序来定位属性的,需要在不同的语言中都要维护同样的模型以及模型中属性的顺序。
3.不支持模型嵌套。msgpack无法支持在模型中包含和嵌套其他自定义的模型(如weibo模型中包含comment的列表)。
Github地址:https://github.com/msgpack
官网:https://msgpack.org/
官方文档:https://msgpack.org/
影响序列化性能的关键因素:
序列化后的码流大小(网络带宽的占用)
序列化的性能(CPU资源占用)
是否支持跨语言(异构系统的对接和开发语言切换)