Java 序列化反序列化框架比较

文章目录

  • 一、简介
  • 二、序列化框架
    • 1、JDK
    • 2、XML序列化
    • 3、JSON序列化
    • 4、Hessian
    • 5、Avro序列化
    • 6、Kyro序列化
    • 7、Protostuff
  • 三、序列化框架对比测试
    • 1、对象准备
    • 2、JDK方式
    • 3、FastJson方式
    • 4、Hessian方式
    • 5、Protostuff方式
    • 6、测试代码
  • 四、总结
  • 五、序列化应用场景
  • 六、注意事项


一、简介

序列化:将Java对象转化成字节数组。

反序列化:将字节数组转化成Java对象。


影响序列化选择有两个因素:

  • 序列化之后码流的大小,如果太大,那么将会影响网络传输的性能。

  • 序列化和反序列化过程的性能。


二、序列化框架

1、JDK


2、XML序列化

XML 协议,良好的可读性,自由度极高的扩展性,成了很长一段时间的序列化标准规范;

可以说XML序列化是开发中最常见也是发展时间最久的协议,并且支持跨进程和跨语言交互。

但是缺陷也很明显,即XML规范下的每一个属性和值都是固定的标签形式,导致序列化后的字节流文件很大,而且解析复杂,效率很低。

方案:最常见的是 XStreamJava自带的XML序列化和反序列化 两种。


3、JSON序列化

XML序列化发展了多年后,也浮现了一些问题,比如开发并不简便,解析XML复杂度较高,还有XML的标准规范比较多,自由度过高,导致很难有效的指定格式校验等,于是一种新的 轻量级的序列化交互的方案--JSON(JavaScript Object Notation) 出现了,相对于XML来说,json格式语法简单,自由度较高,有很高的可读性,并且在JSON序列化后的字节流小于XML序列化的结果,解析起来更方便,于是基于JSON的接口成了新的标准规范之一。

方案:最常见的Jackson、阿里巴巴开源的FastJson、谷歌的GSON


4、Hessian

简单说来,Hessian 是一个轻量级的RPC框架。

它基于HTTP协议传输,使用Hessian二进制序列化,对于数据包比较大的情况比较友好。


5、Avro序列化

Avro 序列化设计初衷是为了支持大批量数据交换的应用,支持二进制序列化方式,并且自身提供了动态语言支持,可以更加便捷、快速处理大批量的Avro数据


6、Kyro序列化

Kyro序列化 是主流的比较成熟的序列化方案之一,目前广泛使用在大数据组件中,比如Hive、Storm等,性能比起Hessian还要优越,但是缺陷较明显,不支持跨语言交互,在dubbo2.6.x版本开始已经加入了Kyro序列化的支持。


7、Protostuff

Protobuf 是谷歌提出的序列化方案,不同的是此方案 独立于语言、平台,谷歌提供了多个语言如java、c、go、python等语言的实现,也提供了多平台的库文件支持,使用比较广泛,优点在于 性能开销很小,压缩率很高,但是缺陷也很明显,可读性很差,并且protobuf需要使用特定语言的库进行翻译转换,使用起来较为麻烦。


三、序列化框架对比测试

下面我们挑选 JDKFastJsonHessianProtostuff 四种序列化方式进行对比测试。


测试之前我们需要准备序列化对象,对象在序列化时,需要注意以下几个关键字:

  • Serializable:接口,是一个标志性接口,标识可以在 JVM 中进行序列化,JVM 会为该类自动生成一个序列化版本号。参与序列化与反序列化的类必须实现 Serializable 接口。

  • serialVersionUID:类属性,序列化版本号,用于给 JVM 区别同名类,没有提供版本号,JVM会默认提供序列化版本号。

  • transient:关键字,当序列化时,不希望某些属性参与,则可以使用这个关键字标注该属性。


1、对象准备

SHeader.java

package com.springboottest.serialize.model;import java.io.Serializable;
import java.util.Map;public class SHeader implements Serializable {private int code;private int length;private String type;private Map<String, Object> attach;public int getCode() {return code;}public void setCode(int code) {this.code = code;}public int getLength() {return length;}public void setLength(int length) {this.length = length;}public String getType() {return type;}public void setType(String type) {this.type = type;}public Map<String, Object> getAttach() {return attach;}public void setAttach(Map<String, Object> attach) {this.attach = attach;}
}

SRequest.java

package com.springboottest.serialize.model;import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;public class SRequest implements Serializable {private String version;private SHeader header;private Object body;public String getVersion() {return version;}public void setVersion(String version) {this.version = version;}public SHeader getHeader() {return header;}public void setHeader(SHeader header) {this.header = header;}public Object getBody() {return body;}public void setBody(Object body) {this.body = body;}public static class SRequestBuilder{public static SRequest build(){SRequest request = new SRequest();request.setVersion("1.0.0");SHeader header = new SHeader();header.setCode(200);header.setLength(202);header.setType("Object");Map<String, Object> attach = new HashMap<String, Object>();attach.put("name", "davis");attach.put("age", 30);header.setAttach(attach);request.setHeader(header);request.setBody("{\"code\":200, \"msg\":\"success\"}");return request;}}
}

序列化接口 AbstractSerialize.java

package com.springboottest.serialize;public abstract class AbstractSerialize {public abstract <T> byte[] serialize(T obj);public abstract <T> T deserialize(byte[] data, Class<T> clazz);
}

2、JDK方式

JDK自带的序列化反序列化方式需要用到的两个类:

  • ObjectOutputStream:IO类,包含序列化对象的方法,writeObject();

  • ObjectInputStream:IO类,包含反序列化对象的方法,readObject();


具体代码 JDKSerialize.java

package com.springboottest.serialize;import com.springboottest.serialize.model.SRequest;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;public class JDKSerialize extends AbstractSerialize{@Overridepublic <T> byte[] serialize(T obj) {if (obj  == null){throw new NullPointerException();}ByteArrayOutputStream bos = new ByteArrayOutputStream();try {ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(obj);return bos.toByteArray();} catch (Exception ex) {ex.printStackTrace();}return new byte[0];}@Overridepublic <T> T deserialize(byte[] data, Class<T> clazz) {ByteArrayInputStream bis = new ByteArrayInputStream(data);try {ObjectInputStream ois = new ObjectInputStream(bis);T obj = (T)ois.readObject();return obj;} catch (Exception ex) {ex.printStackTrace();}return  null;}
}

3、FastJson方式

FastJson 引入方式(pom.xml):

<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.56</version>
</dependency>

具体代码 FastjsonSerialize.java

package com.springboottest.serialize;
import com.alibaba.fastjson.JSON;public class FastjsonSerialize extends AbstractSerialize{@Overridepublic <T> byte[] serialize(T obj) {if (obj  == null){throw new NullPointerException();}String json = JSON.toJSONString(obj);byte[] data = json.getBytes();return data;}@Overridepublic <T> T deserialize(byte[] data, Class<T> clazz) {T obj = JSON.parseObject(new String(data),clazz);return obj;}
}

4、Hessian方式

Hessian 引入方式(pom.xml):

<dependency><groupId>com.caucho</groupId><artifactId>hessian</artifactId><version>4.0.60</version>
</dependency>

具体代码 HessianSerialize.java

package com.springboottest.serialize;import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;public class HessianSerialize extends AbstractSerialize{@Overridepublic <T> byte[] serialize(T obj) {if (obj  == null){throw new NullPointerException();}try{ByteArrayOutputStream bos = new ByteArrayOutputStream();HessianOutput ho = new HessianOutput(bos);ho.writeObject(obj);return  bos.toByteArray();} catch(Exception ex){}return new byte[0];}@Overridepublic <T> T deserialize(byte[] data, Class<T> clazz) {if (data == null){throw  new  NullPointerException();}try{ByteArrayInputStream bis = new ByteArrayInputStream(data);HessianInput hi = new HessianInput(bis);return (T)hi.readObject();} catch(Exception ex){ex.printStackTrace();}return null;}
}

5、Protostuff方式

Protostuff 引入方式(pom.xml):

<dependency><groupId>io.protostuff</groupId><artifactId>protostuff-core</artifactId><version>1.6.0</version><scope>compile</scope>
</dependency><dependency><groupId>io.protostuff</groupId><artifactId>protostuff-runtime</artifactId><version>1.6.0</version>
</dependency>

具体代码 ProtostuffSerialize.java

package com.springboottest.serialize;import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;public class ProtostuffSerialize extends AbstractSerialize{/*** 避免每次序列化都重新申请Buffer空间*/private static LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);/*** 缓存Schema*/private static Map<Class<?>, Schema<?>> schemaCache = new ConcurrentHashMap<Class<?>, Schema<?>>();@Overridepublic <T> byte[] serialize(T obj) {if (obj  == null){throw new NullPointerException();}Class<T> clazz = (Class<T>) obj.getClass();Schema<T> schema = getSchema(clazz);byte[] data;try {data = ProtostuffIOUtil.toByteArray(obj, schema, buffer);} finally {buffer.clear();}return data;}@Overridepublic <T> T deserialize(byte[] data, Class<T> clazz) {Schema<T> schema = getSchema(clazz);T obj = schema.newMessage();ProtostuffIOUtil.mergeFrom(data, obj, schema);return obj;}private static <T> Schema<T> getSchema(Class<T> clazz) {Schema<T> schema = (Schema<T>) schemaCache.get(clazz);if (schema == null) {//这个schema通过RuntimeSchema进行懒创建并缓存//所以可以一直调用RuntimeSchema.getSchema(),这个方法是线程安全的schema = RuntimeSchema.getSchema(clazz);if (schema != null) {schemaCache.put(clazz, schema);}}return schema;}
}

6、测试代码

SerializeTest.java

package com.springboottest.serialize;import com.springboottest.serialize.model.SRequest;public class SerializeTest {public static void main(String[] args) {System.out.println("------------ JDK Serialize ------------");test(new JDKSerialize());System.out.println();System.out.println("------------ FastJson Serialize ------------");test(new FastjsonSerialize());System.out.println();System.out.println("------------ Hessian Serialize ------------");test(new HessianSerialize());System.out.println();System.out.println("------------ Protostuff Serialize ------------");test(new ProtostuffSerialize());System.out.println();}public static void test(AbstractSerialize abstractSerialize){for(int i=0;i<5;i++){testSerialize(abstractSerialize);System.out.println("----------------------");}}public static void testSerialize(AbstractSerialize abstractSerialize){AbstractSerialize serialize = abstractSerialize;SRequest request = SRequest.SRequestBuilder.build();SRequest result = null;byte[] bytes = serialize.serialize(request);System.out.println("字节长度:" + bytes.length);int count = 100;long seriDuration = 0, deseriDuration = 0;long start = 0, end = 0;for(int i=0;i<count;i++){start = System.nanoTime();bytes = serialize.serialize(request);end = System.nanoTime();seriDuration += (end - start);start = System.nanoTime();result = serialize.deserialize(bytes, SRequest.class);end = System.nanoTime();deseriDuration += (end - start);}System.out.println("总次数:" + count);System.out.println("序列化总耗时:" + seriDuration/1000 + "us");System.out.println("反序列化总耗时:" + deseriDuration/1000 + "us");System.out.println("总耗时:" + (seriDuration + deseriDuration)/1000 + "us");}
}

输出结果(序列化和反序列化100次):

------------ JDK Serialize ------------
字节长度:526
总次数:100
序列化总耗时:10616us
反序列化总耗时:73580us
总耗时:84197us
----------------------
字节长度:526
总次数:100
序列化总耗时:5269us
反序列化总耗时:18589us
总耗时:23859us
----------------------
字节长度:526
总次数:100
序列化总耗时:3492us
反序列化总耗时:12892us
总耗时:16385us
----------------------
字节长度:526
总次数:100
序列化总耗时:3483us
反序列化总耗时:9978us
总耗时:13462us
----------------------
字节长度:526
总次数:100
序列化总耗时:2414us
反序列化总耗时:9121us
总耗时:11535us
---------------------------------- FastJson Serialize ------------
字节长度:150
总次数:100
序列化总耗时:7581us
反序列化总耗时:42988us
总耗时:50570us
----------------------
字节长度:150
总次数:100
序列化总耗时:8240us
反序列化总耗时:10351us
总耗时:18592us
----------------------
字节长度:150
总次数:100
序列化总耗时:7231us
反序列化总耗时:9794us
总耗时:17026us
----------------------
字节长度:150
总次数:100
序列化总耗时:5001us
反序列化总耗时:6936us
总耗时:11938us
----------------------
字节长度:150
总次数:100
序列化总耗时:3552us
反序列化总耗时:5458us
总耗时:9011us
---------------------------------- Hessian Serialize ------------
字节长度:243
总次数:100
序列化总耗时:14385us
反序列化总耗时:30783us
总耗时:45168us
----------------------
字节长度:243
总次数:100
序列化总耗时:9114us
反序列化总耗时:13857us
总耗时:22972us
----------------------
字节长度:243
总次数:100
序列化总耗时:7527us
反序列化总耗时:12365us
总耗时:19892us
----------------------
字节长度:243
总次数:100
序列化总耗时:5211us
反序列化总耗时:9830us
总耗时:15042us
----------------------
字节长度:243
总次数:100
序列化总耗时:6858us
反序列化总耗时:10122us
总耗时:16981us
---------------------------------- Protostuff Serialize ------------
字节长度:86
总次数:100
序列化总耗时:2404us
反序列化总耗时:5213us
总耗时:7617us
----------------------
字节长度:86
总次数:100
序列化总耗时:1450us
反序列化总耗时:1668us
总耗时:3119us
----------------------
字节长度:86
总次数:100
序列化总耗时:1183us
反序列化总耗时:1440us
总耗时:2624us
----------------------
字节长度:86
总次数:100
序列化总耗时:1038us
反序列化总耗时:1316us
总耗时:2355us
----------------------
字节长度:86
总次数:100
序列化总耗时:991us
反序列化总耗时:1509us
总耗时:2501us
----------------------

四、总结

序列化方式字节流大小(B)100次耗时(us)1000次耗时(us)10000次耗时(us)
JDK52611 ~ 2365 ~ 78215 ~ 385
FastJson1509 ~ 1811 ~ 4620 ~ 40
Hessian24315 ~ 2345 ~ 63241 ~ 355
Protostuff862 ~ 74 ~ 1011 ~ 20

注:

  • 字节流大小单位:B(字节)
  • 序列化和反序列化总耗时单位:us(微秒)

从图表中可以看出:

  • JDK方式的码流最大,不利于网络传输

  • 从整体来看,Prorostuff的码流最小,序列化性能最好


五、序列化应用场景

  • 序列化会将内存中对象的状态转换成二进制文件保存到磁盘当中,当再次使用时会从磁盘中读取该二进制文件,将 Java 对象的状态恢复到内存中。

  • 当你想把内存中的对象保存到磁盘文件或数据库中时可以使用序列化。

  • 当你想在网络传输中传送 Java 对象时,可以使用序列化。

  • 当你想通过 RMI 传输对象时,可以使用序列化。


六、注意事项

  • 序列化只会保存对象的属性状态,不会保存对象中的方法。

  • 父类实现了 Serializable接口,则其子类也自动实例化了该接口,也就是说子类不用显式实现 Serializable 接口也能参与序列化和反序列化。

  • 一个 对象 A 的实例变量引用了其他 对象 B,在 A 对象实例化的过程中 ,也会序列化 B ,前提是 A、B 两个类都实现了 Serializable 接口。

  • 当一个类实现 Serializable 接口时,最好手动指定一个序列化版本号(serialVersionUID),避免修改源代码后导致反序列化出现异常。

  • 当一个类对象会被多次重复使用,且一般不会对其属性做修改,就可以对其进行序列化。例如数据库操作中的实体类。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/446677.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++primer 第 2 章 变量和基本类型

2.1 基本内置类型 算术类型&#xff08;arithmetictype&#xff09;和空类型&#xff08;void&#xff09;在内的基本数据类型。其中算术类型包含了字符、整型数、布尔值和浮点数。空类型不对应具体的值&#xff0c;仅用于一些特殊的场合&#xff0c;例如最常见的是&#xff0…

codeforces 58A-C语言解题报告

58A题目网址 题目解析 1.输入字符串,问如果删去其中的一些自发,能否得到hello,如果能就输出YES,否则输出NO 举例: 输入: ahhellllloou 输出: YES 2.注意点: 因为C语言没有java中的匹配字符串,则新建立一个 word[6]“hello”; 在循环中使用word去与s匹配,当匹配到了就 count…

ClickHouse 客户端命令

文章目录一、简介二、常用命令1、连接命令2、SQL语法&#xff08;1&#xff09;查看数据库列表&#xff08;2&#xff09;查看当前使用的数据库&#xff08;3&#xff09;查看数据库中表列表&#xff08;4&#xff09;创建数据库&#xff08;5&#xff09;创建表&#xff08;6&…

2000年考研英语阅读理解文章三

文章详细解析 注意点 1.当作者在文章中写到:实际问题是:我们从根本上改变了吗? 说明:我们没有发生根本上的改变,作者不同意前文中的未来派诗歌 知识点 ----单词 unhampered adj无阻碍的 finite adj有限的 ink n墨水 corresponding adj相应的,符合的 upsetting adj令人生厌…

SpringBoot 集成Mybatis

文章目录一、创建SpringBoot项目二、添加Mybatis相关依赖三、数据源配置四、创建事务的模型实体类五、创建和数据库交互联系的映射关系类六、创建业务接口和实现类七、创建控制器类八、请求验证一、创建SpringBoot项目 如何创建详见&#xff1a;IDEA 创建 SpringBoot 项目 二、…

C++primer 第 3 章 字符串、向量和数组 3.1 命名空间的using声明 3.2标准库类型string

引言 除了第2章介绍的内置类型之外,C语言还定义了 -个内容丰富的抽象数据类型库。其中,string和 vector是两种最重耍的标准库类型&#xff0c;前者支持可变长字符串&#xff0c;后者则 表示可变长的集合。还有…种标准库类型是迭代器&#xff0c;它是string和vector的配套类型…

ClickHouse 四舍五入函数

文章目录一、round(x[,N])二、floor(x[,N])三、ceil(x[,N]),ceiling(x[,N])四、trunc(x[, N]), truncate(x[, N])一、round(x[,N]) 说明&#xff1a;将值取整到指定的小数位数&#xff0c;该函数按顺序返回最近的数字。 语法&#xff1a; round(expression [, decimal_place…

codeforces 59A-C语言解题报告

59A题目网址 题目解析 1.输入字符串,如果大写字母最多,则全部输出为大写;如果小写字母多或大小写字母一样多,则全部输出为小写 举例: 输入: maTRIx 输出: matrix 2.使用a,b两个变量去记录大小写字母的数量 代码 #include<stdio.h> #include<stdlib.h> #includ…

C++primer 第 3 章 字符串、向量和数组 3 . 3 标准库类型vector

标准库类型vector表示对象的集合&#xff0c;其中所有对象的类型都相同。集合中的每个对象都有一个与之对应的索引&#xff0c;索引用于访问对象。因为vector"容纳着”其他对象&#xff0c;所以它也常被称作容器(container).第 II部将对容器进行更为详细的介绍。 要想使用…

SpringBoot AOP切面实现

文章目录一、AOP简介二、AOP体系与概念三、AOP实例1、创建SpringBoot工程2、添加依赖3、AOP相关注解3.1、Aspect3.2、Pointcut3.2.1、execution()3.2.2、annotation()3.3、Around3.4、Before3.5、After3.6、AfterReturning3.7、AfterThrowing一、AOP简介 AOP&#xff08;Aspec…

英语口语-文章朗读Week8 Friday

文章 It is a phenomenon that people are losing trust in each other in today’s society. Some people become selfish,and for interest, they are likely to betray their colleagues,friends, and even their relatives. They tend to cater to those who can benefit …

C++primer 第 3 章 字符串、向量和数组 3 . 4 迭代器介绍

3.4迭代器介绍 我们已经知道可以使用下标运算符来访问string对象的字符或vector对象的元素&#xff0c;还有另外一种更通用的机制也可以实现同样的目的&#xff0c;这就是迭代器&#xff08;iterator&#xff09;。在第II部分中将要介绍&#xff0c;除了vector之外&#xff0c…

ClickHouse 函数

文章目录一、日期函数1、时间或日期截取函数&#xff08;返回非日期&#xff09;2、时间或日期截取函数&#xff08;返回日期&#xff09;3、日期或时间日期生成函数二、类型转化类函数1、精度保留&#xff08;非四舍五入&#xff09;2、字符串转化为整数&#xff08;非整数的字…

英语口语-文章朗读Week9 TuesDay

朗读文章 People living in ancient times had no alternative but to do housework manually. They fire the wood when they cook,they hand wash clothes with hands; they sweep the floor with brooms. Now, modern inventions come as a great relief to people. We co…

SpringBoot @Value注解

目录一、非配置文件注入1、注入普通字符串2、注入JAVA系统变量3、注入表达式4、注入其他Bean属性5、注入文件资源6、注入URL资源二、通过配置文件注入1、注入普通字符串2、注入基本类型3、注入数组类型4、注入List类型5、注入Map类型一、非配置文件注入 1、注入普通字符串 直…

C++primer 第 3 章 字符串、向量和数组 3 . 5 数组

3.5数组 数组是一种类似于标准库类型vector&#xff08;参见3.3节&#xff0c;第86页&#xff09;的数据结构&#xff0c;但是在性能和灵活性的权衡上又与vector有所不同。与vector相似的地方是&#xff0c;数组也是存放类型相同的对象的容器&#xff0c;这些对象本身没有名字…

codeforces 122A-C语言解题报告

122A题目网址 题目解析 1.输入数字(在1000以内),若能被4,7幸运数整除或只含4,7则输出YES,否则输出NO 举例: 输入: 107 输出: NO 2.解题关键: 1)使用列举法,把所有符合的幸运数列出来(int number[]) 1—2 2–224 3–22*28 24814个 2)若n是幸运数中的一个或n%幸运数0,则为YES…

SpringBoot @Value给静态变量注入值

文章目录一、简介二、Value给静态变量注入值方案一&#xff1a;set()方法设置方案二&#xff1a;PostConstruct注解修饰的方法中进行赋值三、总结一、简介 SpringBoot 中给普通变量注入值只需在变量上添加 Value 注解即可。 application.properties 配置文件有如下配置&#…

C++primer 第 4 章 表达式 4.1基础 4 . 2 算术运算符 4 .3 逻辑和关系运算符 4 . 4 赋值运算符 4 .5 递增和递减运算符 4.6成员访问运算符

表达式由一个或多个运算对象(operand)组成&#xff0c;对表达式求值将得到一个结果(result)字面值和变量是最简单的表达式(expression),其结果就是字面值和变量的值。把一个运算符(operator)和一个或多个运算对象组合起来可以生成较复杂的表达式 4.1基础 有几个基础概念对表达…

codeforces 266B-C语言解题报告

266B题目网址 题目解析 输入n,t,排队情况s,输出第t次循环后,排队情况 举例: 输入: 5 1 BGGBG 输出: GBGGB 2.输入的n代表排队的人数,t代表整个循环t次之后再输出结果 3.注意点: 使用while()大循环去控制t次的循环,使用for()内层循环去遍历整个字符串 如果if(s[j]‘B’&…