目录

1、序列化与反序列化的作用

2、序列化协议

2.1 JDK序列化协议

2.1.1  Externalizable与Serializable的异同

2.2 Google ProtocolBuf 协议

2.3  ProtoStuff 协议

2.4 Kryo 协议

2.5  XML协议

2.6  JSON协议

2.6.1 开源的Jackson

2.6.2 Google的Gson

2.6.3 阿里巴巴的FastJson

2.6.4 总结

2.7 FST

2.8 hessian

2.9 Facebook的Thrift

3、序列化与单例模式

4、序列化在Java框架中的应用

4.1 Dubbo 序列化

4.2 Netty 编解码


1、序列化与反序列化的作用

序列化:将堆中对象转化成可以存储或网络传输的二进制字节序列的过程。

反序列化:将来自于网络传输或者存储的字节序列恢复成Java对象的过程。

当Java对象需要进行本地文件存储时,需要将Java对象进行序列化;
当Java对象需要进行网络传输,如 rpc调用、数据库持久化,需要将Java对象进行序列化;

参考文档:JAVA基础4---序列化和反序列化深入整理(JDK序列化)

2、序列化协议

2.1 JDK序列化协议

下面是JDK序列化的一个示例:

package com.autocoding.serializable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class JdkSerializer {public static void serialize(Student student) throws FileNotFoundException, IOException {ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("D:\\student.txt")));oo.writeObject(student);oo.close();}public static Student deserialize() throws IOException, Exception {ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("D:\\student.txt")));Student student = (Student) ois.readObject();return student;}public static void main(String[] args) {Student student = new Student();student.setId("1000");student.setName("李桥");try {// 序列化JdkSerializer.serialize(student);// 反序列化Student serializeStudent = JdkSerializer.deserialize();System.out.println(serializeStudent.getName());} catch (Exception e) {e.printStackTrace();}}private static class Student implements Serializable {private static final long serialVersionUID = 8888L;private String id;private String name;//构造器访问控制符为私有的,埋下一个伏笔privateStudent() {}public String getId() {return id;}public void setId(String id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}}
}

下面是序列化到文件 D:\\student.txt 的内容:

比较粗略的可以看到序列化之后,存储了对象的类型信息,字段的类型信息。

2.1.1  Externalizable与Serializable的异同

Externalizable接口扩展自java.io.Serializable接口。实现java.io.Serializable即可获得对类的对象的序列化功能。而Externalizable可以通过writeExternal()和readExternal()方法可以指定序列化哪些属性。

  1. 序列化内容
    Externalizable自定义序列化可以控制序列化的过程和决定哪些属性不被序列化。
  2. Serializable反序列化时不会调用默认的构造器,而Externalizable序列化时会调用默认Public无参构造器的
  3. 使用Externalizable时,必须按照写入时的确切顺序读取所有字段状态。否则会抛出java.io.EOFException。而Serializable接口没有这个要求。

参考文档:Externalizable的用法及与Serializable的异同

2.2 Google ProtocolBuf 协议

ProtocolBuff是谷歌推出的一种序列化协议,是一个非常高效的序列化协议。相对于Java这种数据类型固定长度的序列化(int 4字节, long 8字节), PB提供了可伸缩性的数据类型(int 1-5字节)。
当数据比较小的时候int只占用1个字节, 而大部分场景下, 数据都是很小的, 不然jdk本身也不会缓存 -127~128了。
         参考文档:Protocol Buffer序列化对比Java序列化.
                           性能最好的序列化反序列化,Protobuf的用法

优点:1、序列化后码流小,性能高
           2、通过标识字段的顺序,可以实现协议的前向兼容
           3、结构化数据存储格式(XML JSON等),结构化的文档更容易管理和维护

缺点:1、需要依赖于工具生成代码
           2、支持的语言相对较少,官方只支持Java 、C++ 、Python
           3、序列化数据只包含数据本身以及类的结构,不包括类型标识和程序集信息
使用场景: 对性能要求高的RPC调用。

Google 的ProtocolBuff不好用,需要自己编码.proto文件,现在推荐比较好用的类库Baidu的JProtoBuf。
jprotobuf是针对Java程序开发一套简易类库,目的是简化java语言对protobuf类库的使用使用jprotobuf可以无需再去了解proto文件操作与语法,直接使用java注解定义字段类型即可。
Github: https://github.com/jhunters/jprotobuf

2.3  ProtoStuff 协议

protostuff是一个基于protobuf实现的序列化协议,它较于protobuf最明显的好处是,在几乎不损耗性能的情况下做到了不用我们写.proto文件来实现序列化。 
      代码示例:

package com.autocoding.serializable;
import java.util.Arrays;
import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtobufIOUtil;
import com.dyuproject.protostuff.runtime.RuntimeSchema;
public class ProtoStuffSerializer {public static <T> byte[] serialize(T t, Class<T> clazz) {return ProtobufIOUtil.toByteArray(t, RuntimeSchema.createFrom(clazz),LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE));}public static <T> T deserialize(byte[] data, Class<T> clazz) {RuntimeSchema<T> runtimeSchema = RuntimeSchema.createFrom(clazz);T t = runtimeSchema.newMessage();ProtobufIOUtil.mergeFrom(data, t, runtimeSchema);return t;}public static void main(String[] args) {Student student = new Student();student.setId("1000");student.setName("李桥");try {// 序列化byte[] bytes = ProtoStuffSerializer.serialize(student, Student.class);System.out.println("序列化之后:" + Arrays.toString(bytes));// 反序列化Student serializeStudent = ProtoStuffSerializer.deserialize(bytes, Student.class);System.out.println(serializeStudent.getName());} catch (Exception e) {e.printStackTrace();}}private static class Student {private String id;private String name;private Student() {}public String getId() {return id;}public void setId(String id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}}
}

程序运行结果:

序列化之后:[10, 4, 49, 48, 48, 48, 18, 6, -26, -99, -114, -26, -95, -91]
李桥

参考文档:初探Protostuff的使用      
      github: https://github.com/protostuff/protostuff

2.4 Kryo 协议

Kryo是一种非常成熟的序列化实现,已经在Twitter、Groupon、 Yahoo以及多个著名开源项目(如Hive、Storm)中广泛的使用。而FST是一种较新的序列化实现,目前还缺乏足够多的成熟使用案例,但它还是非 常有前途的,
下面我们比较下,java原生序列化Kryo序列化性能比较。Kryo的性能是JDK序列化协议性能的10倍-100倍之间。

参考文档:java原生序列化和Kryo序列化性能比较

2.5  XML协议

优点:1、人机可读性好,可指定元素或特性的名称
           2、数据可以有层次结构

缺点:1、只能序列化属性和字段、不能序列化方法
           2、文件庞大,文件格式复杂,传输占带宽
           3、序列化数据只包含数据本身以及类的结构,不包括类型标识和程序集信息

使用场景: 配置文件存储数据

xml类库主要有:dom4j、Jdom、xstrem                     参考文档: Java 主流开源类库解析 XML

2.6  JSON协议

优点:1、数据格式比较简单,易于读写
           2、序列化后数据较小,可扩展性好,兼容性好
           3、与XML相比,其协议比较简单,解析速度比较快

缺点:1、数据的描述性比XML差
           2、不适合性能要求为ms级别的情况
           3、额外空间开销比较大

使用场景: 传输数据量相对小,实时性要求相对低(例如秒级别)的服务

2.6.1 开源的Jackson

相比json-lib框架,Jackson所依赖的jar包较少,简单易用并且性能也要相对高些。而且Jackson社区相对比较活跃,更新速度也比较快。
Jackson对于复杂类型的json转换bean会出现问题,一些集合Map,List的转换出现问题。Jackson对于复杂类型的bean转换Json,转换的json格式不是标准的Json格式

2.6.2 Google的Gson

Gson是目前功能最全的Json解析神器,Gson当初是为因应Google公司内部需求而由Google自行研发而来,
但自从在2008年五月公开发布第一版后已被许多公司或用户应用。

Gson的应用主要为toJson与fromJson两个转换函数,无依赖,不需要例外额外的jar,能够直接跑在JDK上。
而在使用这种对象转换之前需先创建好对象的类型以及其成员才能成功的将JSON字符串成功转换成相对应的对象。
类里面只要有get和set方法,Gson完全可以将复杂类型的json到bean或bean到json的转换,是JSON解析的神器。
Gson在功能上面无可挑剔,但是性能上面比FastJson有所差距。

2.6.3 阿里巴巴的FastJson

Fastjson是一个Java语言编写的高性能的JSON处理器,由阿里巴巴公司开发。无依赖,不需要例外额外的jar,能够直接跑在JDK上。FastJson在复杂类型的Bean转换Json上会出现一些问题,
可能会出现引用的类型,导致Json转换出错,需要制定引用。FastJson采用独创的算法,将parse的速度提升到极致,超过所有json库。

2.6.4 总结

综上对各种Json技术的比较,在项目选型的时候可以使用Google的Gson和阿里巴巴的FastJson两种并行使用,如果只是功能要求,没有性能要求,可以使用google的 Gson ,
如果有性能上面的要求可以使用Gson将bean转换json确保数据的正确,使用 FastJson 将Json转换Bean。

2.7 FST

高性能序列化框架FST:http://liuyieyer.iteye.com/blog/2136240

FST 是完全兼容JDK序列化协议的系列化框架,序列化速度大概是JDK的4-10倍,序列化之后码流大小是JDK大小的1/3左右。

2.8 hessian

Hessian类似Web Service,是一种高效简洁的远程调用框架。Hessian的主页:http://hessian.caucho.com/
相比WebService,Hessian更简单、快捷。采用的是二进制RPC协议(Binary),因为采用的是二进制协议,所以它很适合于发送二进制数据。Hessian通常通过Web应用来提供服务,因此非常类似于WebService。
只是它不使用SOAP协议。    Hessian通过Servlet提供远程服务。需要将匹配某个模式的请求映射到Hessian服务。Spring的DispatcherServlet可以完成该功能,DispatcherServlet可将匹配模式的请求转发到Hessian服务。
Hessian的server端提供一个servlet基类, 用来处理发送的请求,而Hessian的这个远程过程调用,完全使用动态代理来实现的,,推荐采用面向接口编程,因此,Hessian服务建议通过接口暴露。    
Hessian处理过程示意图:    
客户端——>序列化写到输出流——>远程方法(服务器端)——>序列化写到输出流 ——>客户端读取输入流——>输出结果

Hessian的有着不同语言的版本:Java、Flash、Python、C++、.NET C#、D、Erlang、PHP、Ruby,Delphi等等,以后也许会更多更多。Java版的Hessian性能稍逊于RMI。Hessian最大的优势就是简单而且强大!

2.9 Facebook的Thrift

Thrift源于Facebook,2007年Facebook将Thrift做为一个开源项目交给了apache基金会。
特点:

  • 1、 Thrift支持多种语言(C++,C#,Cocoa,Erlag,Haskell,java,Ocami,Perl,PHP,Python,Ruby,和SmallTalk)
  • 2、Thrift适用了组建大型数据交换及存储工具,对于大型系统中的内部数据传输,相对于Json和xml在性能上和传输大小上都有明显的优势。
  • 3、Thrift支持三种比较典型的编码方式。(通用二进制编码,压缩二进制编码,优化的可选字段压缩编解码)

3、序列化与单例模式

在将二进制流反序列化时,通过二进制中的类型信息,进行反射,获取Class对象,然后调用 newInstance()方法来创建对象,请注意newInstance()方法会调用该Class对象的无参构造器(private、public都可以),这样就创建了一个
对象实例,这样很可能会破坏单例模式中的该类只能保留一个实例的约束,为了保证单例模式真正的安全,我们一般使用枚举类型来实现真正的单例模式。

在effective java(这本书真的很棒)中说道,最佳的单例实现模式就是枚举模式。利用枚举的特性,让JVM来帮我们保证线程安全和单一实例的问题。除此之外,写法还特别简单。

public enum Singleton {INSTANCE;public void doSomething() {System.out.println("doSomething");}
}

使用枚举来实现单例模式可以很好的解决这种因为反射而带来的多例风险,所以个人推荐使用枚举来实现单例模式。

4、序列化在Java框架中的应用

4.1 Dubbo 序列化

参考文档:Dubbo序列化

在dubbo RPC中,同时支持多种序列化方式,例如:

  1. dubbo序列化:阿里尚未开发成熟的高效java序列化实现,阿里不建议在生产环境使用它
  2. hessian2序列化:hessian是一种跨语言的高效二进制序列化方式。但这里实际不是原生的hessian2序列化,而是阿里修改过的hessian lite,它是dubbo RPC默认启用的序列化方式
  3. json序列化:目前有两种实现,一种是采用的阿里的fastjson库,另一种是采用dubbo中自己实现的简单json库,但其实现都不是特别成熟,而且json这种文本序列化性能一般不如上面两种二进制序列化。
  4. java序列化:主要是采用JDK自带的Java序列化实现,性能很不理想。

在通常情况下,这四种主要序列化方式的性能从上到下依次递减。对于dubbo RPC这种追求高性能的远程调用方式来说,实际上只有1、2两种高效序列化方式比较般配,而第1个dubbo序列化由于还不成熟,所以实际只剩下2可用,所以dubbo RPC默认采用hessian2序列化。

但hessian是一个比较老的序列化实现了,而且它是跨语言的,所以不是单独针对java进行优化的。而dubbo RPC实际上完全是一种Java to Java的远程调用,其实没有必要采用跨语言的序列化方式(当然肯定也不排斥跨语言的序列化)。

最近几年,各种新的高效序列化方式层出不穷,不断刷新序列化性能的上限,最典型的包括:

  • 专门针对Java语言的:Kryo,FST等等
  • 跨语言的:Protostuff,ProtoBuf,Thrift,Avro,MsgPack等等

这些序列化方式的性能多数都显著优于hessian2(甚至包括尚未成熟的dubbo序列化)。

有鉴于此,我们为dubbo引入Kryo和FST这两种高效Java序列化实现,来逐步取代hessian2。

其中,Kryo是一种非常成熟的序列化实现,已经在Twitter、Groupon、Yahoo以及多个著名开源项目(如Hive、Storm)中广泛的使用。而FST是一种较新的序列化实现,目前还缺乏足够多的成熟使用案例,但我认为它还是非常有前途的。

在面向生产环境的应用中,我建议目前更优先选择Kryo。

4.2 Netty 编解码

参考一篇优秀的文章:Netty之编解码技术(四)

 Netty自带的编解码器

Netty提供了编解码器框架,使得编写自定义的编解码器很容易,并且也很容易重用和封装。Netty提供了 io.netty.handler.codec.MessageToByteEncoder和io.netty.handler.codec.ByteToMessageDecoder接口,方便我们扩展编解码。

  • Codec,编解码器

  • Decoder,解码器

  • Encoder,编码器

编解码器Codec

因为编解码器由两部分组成:Decoder(解码器),Encoder(编码器)编码器。负责将消息从字节或其他序列形式转成指定的消息对象,编码器则相反;解码器负责处理“入站”数据,编码器负责处理“出站”数据。编码器和解码器的结构很简单,消息被编码后解码后会自动通过ReferenceCountUtil.release(message)释放,如果不想释放消息可以使用ReferenceCountUtil.retain(message),这将会使引用数量增加而没有消息发布,大多数时候不需要这么做。

Java序列化与反序列化的深度思考相关推荐

  1. java 序列化概念和作用_结合代码详细解读Java序列化与反序列化概念理解

    Java序列化与反序列化是什么?为什么需要序列化与反序列化?如何实现Java序列化与反序列化?本文围绕这些问题进行了探讨. 1.Java序列化与反序列化 Java序列化是指把Java对象转换为字节序列 ...

  2. Java中如何引用另一个类里的集合_【18期】Java序列化与反序列化三连问:是什么?为什么要?如何做?...

    Java序列化与反序列化是什么? Java序列化是指把Java对象转换为字节序列的过程,而Java反序列化是指把字节序列恢复为Java对象的过程: 序列化:对象序列化的最主要的用处就是在传递和保存对象 ...

  3. java序列化与反序列化(转)

    Java序列化与反序列化是什么?为什么需要序列化与反序列化?如何实现Java序列化与反序列化?本文围绕这些问题进行了探讨. 1.Java序列化与反序列化 Java序列化是指把Java对象转换为字节序列 ...

  4. java序列化和反序列化以及序列化ID的作用分析

     java序列化和反序列化 一.概念 java对象序列化的意思就是将对象的状态转化成字节流,以后可以通过这些值再生成相同状态的对象.对象序列化是对象持久化的一种实现方法,它是将对象的属性和方法转化为一 ...

  5. java序列化和反序列化_Java恶意序列化背后的历史和动机

    java序列化和反序列化 与Java的序列化机制相关的问题已广为人知. 有效的Java 1st Edition (第10章)和有效的Java 2nd Edition (第11章)的整个最后一章都专门讨 ...

  6. 教你彻底学会Java序列化和反序列化

    Java序列化是什么? Java序列化是指把Java对象转换为字节序列的过程,Java反序列化是指把字节序列恢复为Java对象的过程.反序列化:客户端重文件,或者网络中获取到文件以后,在内存中重构对象 ...

  7. 理论 | 教你彻底学会Java序列化和反序列化

    这是小小本周的第四篇 Java序列化是什么? Java序列化是指把Java对象转换为字节序列的过程,Java反序列化是指把字节序列恢复为Java对象的过程.反序列化:客户端重文件,或者网络中获取到文件 ...

  8. Java序列化与反序列化(一)

    目录 序列化与反序列化 1.最简单的使用:Serializable 接口 2. 序列化 ID 的问题 3. 静态字段不会序列化 4. 屏蔽字段:transient 5. 父类的序列化 6. 自定义序列 ...

  9. Serializable详解(1):代码验证Java序列化与反序列化

    说明:本文为Serializable详解(1),最后两段内容在翻译上出现歧义(暂时未翻译),将在后续的Serializable(2)文中补充. 介绍:本文根据JDK英文文档翻译而成,本译文并非完全按照 ...

最新文章

  1. php mysql实现每日签到积分_php+mysql+jquery实现日历签到功能
  2. org.eclipse.jdt.internal.compiler包下的类找不到
  3. 在家办公的我,砍需求砍得更狠了
  4. (实战项目三)新浪网分类资讯爬虫
  5. 超强!MDETR:基于Transformer的端到端目标检测神器!开源!
  6. 求素数的三种方法(Java实现)
  7. 总结了Linux系统中常用的命令
  8. 特聘专家朱嘉明:2022,数字经济迈入历史新阶段
  9. nokia手机的含义
  10. intel(r)wireless-ac9462异常//笔记本电脑网络无法连接???吐血后总结 :一次解决,史上最全,N种方案
  11. Linux操作系统 第六章
  12. cpu2017的526.blender编译出错原因
  13. 钕铁硼NdFeB材料各类牌号磁特性大全
  14. airpods二代降噪吗_华强北 苹果二代三代 蓝牙耳机。最新款airpods 不跳电,真降噪。...
  15. MATLAB----模糊神经网络
  16. 普林斯顿 计算机专业排名,普林斯顿计算机系统专业排名2020年
  17. pycharm终端中安装yaml,报错Requirement already satisfied 的解决办法
  18. 试卷分析报告matlab,试卷分析报告(精选10篇)
  19. 笔记本电池常识和THINKPAD电源管理器介绍--能设置充电起点和终点
  20. Zemax学习笔记(15)- ZEMAX设计光谱仪

热门文章

  1. Windows11重置提示找不到恢复环境怎么解决?
  2. YDOOK: ANSYS Maxwell 19 教程11:Maxwell 2D 静电场怎样设置激励源
  3. 利用Python3开发一款小工具(界面的设计)
  4. 线性关系r范围_一个简单线性回归和多项式回归在R中的实现示例
  5. Iptables与Firewalld防火墙(最详细说明)
  6. 解决webpack打包样式url()背景图片问题
  7. 缓和曲线与原曲线任意点坐标计算程序
  8. php网站加广告位,织梦广告位的添加调用
  9. 英特尔服务器主板怎么重装系统,英特尔u盘启动,小编教你英特尔主板怎么在bios中设置u盘启动...
  10. 2009年计算机统考考研试题及参考答案