分布式学习笔记(四)——序列化和反序列化

java序列化和反序列化

Java 平台允许我们在内存中创建可复用的 Java 对象,但一般情况下,只有当 JVM 处于运行时,这些对象才可能存在,但是在实际应用中,可能需要在 JVM 停止运行之后能够保存(持久化)指定的对象,并在将来重新读取被保存的对象。Java 对象序列化就能够帮助我们实现该功能。
简单来说:
序列化是把对象的状态信息转化为可存储或传输的形式过程,也就是把对象转化为字节序列的过程称为对象的序列化。
反序列化是序列化的逆向过程,把字节数组反序列化为对象,把字节序列恢复为对象的过程成为对象的反序列化。

java原生序列化

java主要通过输出流java.io.ObjectOutputStream和对象输入流java.io.ObjectInputStream来实现序列化和反序列化。
java.io.ObjectOutputStream:表示对象输出流 , 它的 writeObject(Object obj)方法可以对参数指定的 obj 对象进行序列化,把得到的字节序列写到一个目标输出流中。
java.io.ObjectInputStream:表示对象输入流 ,它的 readObject()方法源输入流中读取字节序列,再把它们反序列化成为一个对象,并将其返回。
需要注意的是,被序列化的对象需要实现 java.io.Serializable 接口

serialVersionUID

Java 的序列化机制是通过判断类的 serialVersionUID 来验证版本一致性的。在进行反序列化时,JVM 会把传来的字节流中的 serialVersionUID 与本地相应实体类的 serialVersionUID 进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是 InvalidCastException。
serialVersionUID有两种显示声明方式,默认是1L,比如:private static final long serialVersionUID = 1L;
或者根据类名、接口名、成员方法及属性等来生成一个 64 位的哈希字段。如果没有为指定的 class 配置 serialVersionUID,那么 java 编译器会自动给这个 class 进行一个摘要算法,类似于指纹算法,只要这个文件有任何改动,得到的 UID 就会截然不同的。

transient 关键字

transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是0,对象型的是 null。

transient 关键字也可以被绕过,在需要序列化的类里定义两个私有的方法readObject和writeObject(方法名不能变),在这里就可以自己处理需要序列化的属性,即使被transient修饰的属性。示例:name为被transient修饰的属性


    private void writeObject(java.io.ObjectOutputStream s) throws IOException {
        s.defaultWriteObject();
        s.writeObject(name);
    }

    private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
        s.defaultReadObject();
        name=(String)s.readObject();
    }

java序列化总结

  1. Java 序列化只是针对对象的状态进行保存,至于对象中的方法,序列化不关心
  2. 当一个父类实现了序列化,那么子类会自动实现序列化,不需要显示实现序列化接口
  3. 当一个对象的实例变量引用了其他对象,序列化这个对象的时候会自动把引用的对象也进行序列化(实现深度克隆)
  4. 当某个字段被申明为 transient 后,默认的序列化机制会忽略这个字段
  5. 被申明为 transient 的字段,如果需要序列化,可以添加两个私有方法:writeObject 和readObjec

分布式下实现序列化

随着分布式与微服务架构的普及,服务与服务之间的通信成了最基本的需求。这个时候,我们不仅需要考虑通信的性能,也需要考虑到语言多元化问题所以,对于序列化来说,如何去提升序列化性能以及解决跨语言问题,就成了一个重点考虑的问题。java自带的序列化机制存在两个问题:1、序列化的数据比较大,传输效率低:2、其他语言无法识别。

常用的序列化技术

XML

XML 序列化的好处在于可读性好,方便阅读和调试。但是序列化以后的字节码文件比较大,而且效率不高,适用于对性能不高,而且 QPS 较低的企业级内部系统之间的数据交换的场景,同时 XML 又具有语言无关性,所以还可以用于异构系统之间的数据交换和协议。比如我们熟知的 Webservice,就是采用 XML 格式对数据进行序列化的。XML 序列化/反序列化的实现方式有很多,熟知的方式有 XStream 和 Java 自带的 XML 序列化和反序列化两种。

JSON 序列化框架

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,相对于 XML 来说,JSON的字节流更小,而且可读性也非常好。现在 JSON 数据格式在企业运用是最普遍的。常用的JSON 序列化工具有:Jackson、阿里开源的 FastJson、Google 的 GSON。这几种 json 序列化工具中,Jackson 与 fastjson 要比 GSON 的性能要好,但是 Jackson、GSON 的稳定性要比 Fastjson 好。而 fastjson 的优势在于提供的 api 非常容易使用。

Hessian 序列化框架

Hessian 是一个支持跨语言传输的二进制序列化协议,相对于 Java 默认的序列化机制来说,Hessian 具有更好的性能和易用性,而且支持多种不同的语言实际上 Dubbo 采用的就是 Hessian 序列化来实现,只不过 Dubbo 对 Hessian 进行了重构,性能更高

Avro 序列化

Avro 是一个数据序列化系统,设计用于支持大批量数据交换的应用。它的主要特点有:支持二进制序列化方式,可以便捷,快速地处理大量数据;动态语言友好,Avro 提供的机制使动态语言可以方便地处理 Avro 数据。

kyro 序列化框架

Kryo 是一种非常成熟的序列化实现,已经在 Hive、Storm)中使用得比较广泛,不过它不能跨语言. 目前 dubbo 已经在 2.6 版本支持 kyro 的序列化机制。它的性能要优于之前的hessian2。

Protobuf 序列化框架

Protobuf 是 Google 的一种数据交换格式,它独立于语言、独立于平台。Protobuf 使用比较广泛,主要是空间开销小和性能比较好,非常适合用于公司内部对性能要求高的 RPC 调用。 另外由于解析性能比较高,序列化以后数据量相对较少,所以也可以应用在对象的持久化场景中。

序列化技术的选型

序列化技术的选型主要从以下几个方面来考虑
1.、序列化空间开销,也就是序列化产生的结果大小,这个影响到传输的性能
2.、序列化过程中消耗的时长,序列化消耗时间过长影响到业务的响应时间
3.、序列化协议是否支持跨平台,跨语言。因为现在的架构更加灵活,如果存在异构系统通信需求,那么这个是必须要考虑的
4.、可扩展性/兼容性,在实际业务开发中,系统往往需要随着需求的快速迭代来实现快速更新,这就要求我们采用的序列化协议基于良好的可扩展性/兼容性,比如在现有的序列化数据结构中新增一个业务字段,不会影响到现有的服务
5.、技术的流行程度,越流行的技术意味着使用的公司多,那么很多坑都已经淌过并且得到了解决,技术解决方案也相对成熟
6.、学习难度和易用性

选型建议
1.、对性能要求不高的场景,可以采用基于 XML 的 SOAP 协议
2.、对性能和间接性有比较高要求的场景,那么 Hessian、Protobuf、Thrift、Avro 都可以。
3.、基于前后端分离,或者独立的对外的 api 服务,选用 JSON 是比较好的,对于调试、可读性都很不错
4.、Avro 设计理念偏于动态类型语言,那么这类的场景使用 Avro 是可以的
各个序列化技术的性能比较可以参考这个地址: https://github.com/eishay/jvmserializers/wiki

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值