序列化&反序列化(已完结)
2019-11-25 00:13:07 0 举报
AI智能生成
java序列化和反序列化
作者其他创作
大纲/内容
序列化的意义
Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,即,这些对象的生命周期不会比JVM的生命周期更长。但是在现实应用中,就可能要求在JVM停止运行之后能够保存(序列化)指定的对象,并在将来重新读取被保存的对象。Java对象序列化就能够帮助我们实现该功能。
序列化
序列化就是把对象的状态信息转化为可存储或传输的形式过程,也就是把对象转化为字节序列的过程称为对象的序列化
反序列化
序列化的逆向过程,把字节数组反序列化为对象,把字节序列恢复为对象的过程成为对象的反序列化
序列化的挑战
评价一个序列化算法优劣的两个重要指标
数据大小
序列化操作本身的速度及系统 资源开销(CPU 内存)
java序列化的缺点
序列化的数据比较大,传输效率低
其他语言无法识别和对接
如何实现一个序列化
在java中只要一个类实现了java.io.Serializable 接口,那么它就可以被序列化
基于JDK序列化方式实现
JDK提供了Java对象的序列化方式,主要通过输出流 java.io.ObjectOutputStream 和 对象输入流 java.io.ObjectInputStream 来实现。其中,被序列化的对象需要 实现java.io.Serializable接口
序列化的高阶认识
serialVersionUID的作用
Java的序列化机制是通过判断类的 serialVersionUID 来验证版本一致性的。在进行反序列化时,JVM 会把传来的字节流中的 serialVersionUID与本地相应实体类的 serialVersionUID 进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是 InvalidCastException
如果没有为指定的 class 配置 serialVersionUID,那么 java 编译器会自动给这个 class 进行一个摘要算法,类似于指纹算法,只要这个文件有任何改动,得到的 UID 就会截然不同的,可以保证在这么多类中,这个编号是唯一的
serialVersionUID有两种生成方式
默认的1L,比如:private static final long serialVersionUID = 1L;
根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段
当 实 现 java.io.Serializable 接 口 的 类 没 有 显 式 地 定 义 一 个
serialVersionUID 变量时候,Java 序列化机制会根据编译的 Class 自动
生成一个 serialVersionUID 作序列化版本比较用,这种情况下,如果
Class 文件(类名,方法明等)没有发生变化(增加空格,换行,增加注释等
等),就算再编译多次,serialVersionUID 也不会变化的
serialVersionUID 变量时候,Java 序列化机制会根据编译的 Class 自动
生成一个 serialVersionUID 作序列化版本比较用,这种情况下,如果
Class 文件(类名,方法明等)没有发生变化(增加空格,换行,增加注释等
等),就算再编译多次,serialVersionUID 也不会变化的
静态变量序列化
序列化时并不保存静态变量,这其实比较容易理解,序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量。
父类的序列化
一个子类实现了Serializable接口,它的父类都没有实现Serializable接口,在子类中设置父类的成员变量的值,接着序列化该子类对象。再反序列化出来以后输出父类属性的值,结果是
当一个父类没有实现序列化时,子类集成父类并且实现了序列化。在反序列化该子类后,是没办法获取到父类的属性值的。
当一个父类实现序列化,子类自动实现序列化,不需要再显示实现Serializable 接口
当一个对象的实例变量引用了其他对象,序列化
Transient关键字
作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient变量的值设为初始值,如int型的是0.
序列化的存储规则
同一个对象两次(开始写入文件到最终关闭流的这个过程算一次),如果不关闭流写入文件两次,则第二次写入对象时文件只增加5字节。
Java序列化机制为了节省磁盘空间,具有特定的存储规则,当写入文件为同一对象时,并不会再将对象的内容进行存储,而只是再次存储一份引用,上面增加的5字节的存储空间就是新增 引用和一些控制信息的空间。反序列化时,恢复引用关系,该存储规则极大的节省了存储空间。
序列化实现深克隆
在java中存在一个Clineable接口,通过实现这个接口的类都会具备clone的能力,同时clone在内存中进行,在性能方面会比我们直接通过new生成对象要高一些,特别是一些大的对象的生成,性能提升相对比较明显。
常见的序列化技术
java 序列化
优点
java语言本省提供,使用比较方面和简单
缺点
不支持跨语言处理、想能相对不是很好,序列化以后产生的数据相对较大
XML序列化
XML序列化的好处在于可读性好,方面阅读和调试。但是序列化以后的 字节码文件比较大,而且效率不高,适应于对性能不高,而且QPS较低的企业级内部系统之间的数据交换的场景,同时XML又具有语言无惯性,所以还可以用于异构系统之间的数据交换和协议。比如我们熟知的WebService,就是采用XML格式对数据进行序列化的
JSON序列化
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,相对于XML来说,JON的字节流较小,而且可读性也非常好。现在JSON数据格式的其他运用最普遍的。
开源工具
1. Jackson (https://github.com/FasterXML/jackson)
2. 阿里开源的 FastJson (https://github.com/alibaba/fastjon)
3. Google 的 GSON (https://github.com/google/gson)
Hessian 序列化框架子
Hessian是一个支持跨语言传输的二进制序列化协议,相对于Java默认的序列化机制来说,Hessian具有更好的性能和易用性,而且支持对重不同的语言,实际上Dubbo采用的就是Hessian序列化来实现,只不过Dubbo对Hessian进行重构,性能更高。
Protobuf 序列化框架
Protobuf是Google的一种数据交换格式,它独立于语言、独立于平台。
Google 提供了多种语言来实现,比如 Java、C、Go、Python,每一种实现都包含了相应语言的编译器和库文件
Protobuf 使用比较广泛,主要是空间开销小和性能比较好,非常适合用于公司内部对性能要求高的 RPC 调用。 另外由于解析性能比较高,序列化以后数据量相对较少,所以也可以应用在对象的持久化场景中但是但是要使用 Protobuf 会相对来说麻烦些,因为他有自己的语法,有自己的编译器
Protobuf 使用比较广泛,主要是空间开销小和性能比较好,非常适合用于公司内部对性能要求高的 RPC 调用。 另外由于解析性能比较高,序列化以后数据量相对较少,所以也可以应用在对象的持久化场景中但是但是要使用 Protobuf 会相对来说麻烦些,因为他有自己的语法,有自己的编译器
总结
Protocol Buffer 的性能好,主要体现在 序列化后的数据体积小 & 序列化速度快,最终使得传输效率高,其原因如下:
序列化速度快的原因:
编码 / 解码 方式简单(只需要简单的数学运算 = 位移等等)
采用 Protocol Buffer 自身的框架代码 和 编译器 共同完成
序列化后的数据量体积小(即数据压缩效果好)的原因:
序列化后的数据量体积小(即数据压缩效果好)的原因:
采用了独特的编码方式,如 Varint、Zigzag 编码方式等等
采用 T - L - V 的数据存储方式:减少了分隔符的使用 & 数据存储
得紧凑
得紧凑
各个序列化技术的性能比较
序列化技术的选型
技术层面
1. 序列化空间开销,也就是序列化产生的结果大小,这个影响到传输的性能
2. 序列化过程中消耗的时长,序列化消耗时间过长影响到业务的响应时间
3. 序列化协议是否支持跨平台,跨语言。因为现在的架构更加灵活,如果存在异构系统通信需求,那么这个是必须要考虑的
4.可扩展性/兼容性,在实际业务开发中,系统往往需要随着需求的快速迭代来实现快速更新,这就要求我们采用的序化协议基于良好的可扩展性/兼容性,比如在现有的序列化数据结构中新增一个业务字段,不会影响到现有的服务
5. 技术的流行程度,越流行的技术意味着使用的公司多,那么很多坑都已经淌过并且得到了解决,技术解决方案也相对成熟
6. 学习难度和易用性
选型建议
1. 对性能要求不高的场景,可以采用基于 XML 的 SOAP 协议
2. 对性能和间接性有比较高要求的场景,那么Hessian、Protobuf、Thrift、Avro 都可以
3. 基于前后端分离,或者独立的对外的 api 服务,选用 JSON 是比较好的,对于调试、可读性都很不错
4. Avro 设计理念偏于动态类型语言,那么这类的场景使用 Avro 是可
以的
以的
0 条评论
下一页