博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
浅析若干Java序列化工具
阅读量:6548 次
发布时间:2019-06-24

本文共 10410 字,大约阅读时间需要 34 分钟。

  在java中socket传输数据时,数据类型往往比较难选择。可能要考虑带宽、跨语言、版本的兼容等问题。比较常见的做法有:

  1. 采用java对象的序列化和反序列化
  2. 把对象包装成JSON字符串传输
  3. Google工具protoBuf的开源

本文章所需要的序列化jar包都可以下载:。

  为了便于说明各个做法的区别,分别对这三种做法进行阐述。 对UserVo对象进行序列化,class UserVo如下:

package serialize;import java.util.List;public class UserVo{
private String name; private int age; private List
friends; //此处省略Getter和Setter方法}

  初始化一个UserVo实例:

UserVo user = new UserVo();user.setName("zzh");user.setAge(18);UserVo f1 = new UserVo();f1.setName("jj");f1.setAge(17);UserVo f2 = new UserVo();f2.setName("qq");f2.setAge(19);List
friends = new ArrayList
();friends.add(f1);friends.add(f2);user.setFriends(friends);

采用java对象的序列化和反序列化

  这里简单说明一下java序列化所占用字节大小,具体可以参考。

ByteArrayOutputStream os = new ByteArrayOutputStream();        ObjectOutputStream oos = new ObjectOutputStream(os);        oos.writeObject(user);        oos.flush();        oos.close();        System.out.println(os.toByteArray().length);

  序列化大小:205.

  优点:java原生支持,不需要提供第三方的类库,使用比较简单。缺点:无法跨语言,字节数占用比较大,某些情况下对于对象属性的变化比较敏感。


把对象包装成JSON字符串传输

  JSON工具类有许多种,这里列出三个比较流行的json工具类:Jackson,Gson,FastJson.

1.开源的Jackson

  Jackson社区相对比较活跃,更新速度也比较快。Jackson对于复杂类型的json转换bean会出现问题,一些集合Map,List的转换出现问题。Jackson对于复杂类型的bean转换Json,转换的json格式不是标准的Json格式。

package serialize.json;import java.io.IOException;import java.util.ArrayList;import java.util.List;import org.codehaus.jackson.JsonEncoding;import org.codehaus.jackson.JsonGenerator;import org.codehaus.jackson.map.ObjectMapper;import org.junit.After;import org.junit.Before;import org.junit.Test;import serialize.UserVo;public class JacksonTest{
private UserVo user = null; private JsonGenerator jsonGenerator = null; private ObjectMapper objectMapper = null; @Before public void init() { user = new UserVo(); user.setName("zzh"); user.setAge(18); UserVo f1 = new UserVo(); f1.setName("jj"); f1.setAge(17); UserVo f2 = new UserVo(); f2.setName("qq"); f2.setAge(19); List
friends = new ArrayList
(); friends.add(f1); friends.add(f2); user.setFriends(friends); objectMapper = new ObjectMapper(); try { jsonGenerator = objectMapper.getJsonFactory().createJsonGenerator(System.out,JsonEncoding.UTF8); } catch (IOException e) { e.printStackTrace(); } } @After public void destory() { try { if(jsonGenerator != null) { jsonGenerator.flush(); } if(!jsonGenerator.isClosed()) { jsonGenerator.close(); } jsonGenerator = null; objectMapper = null; user = null; } catch (IOException e) { e.printStackTrace(); } } @Test public void writeJson() { try { jsonGenerator.writeObject(user); System.out.println(); System.out.println(objectMapper.writeValueAsBytes(user).length); // System.out.println(objectMapper.writeValueAsString(user).length()); } catch (IOException e) { e.printStackTrace(); } } @Test public void readJson() { String serString = "{\"name\":\"zzh\",\"age\":18,\"friends\":[{\"name\":\"jj\",\"age\":17,\"friends\":null},{\"name\":\"qq\",\"age\":19,\"friends\":null}]}"; UserVo uservo = null; try { uservo = objectMapper.readValue(serString, UserVo.class); } catch (IOException e) { e.printStackTrace(); } System.out.println(uservo.getName()); }}

  序列化大小:111.

  注意到这里Jackson会输出null,在Jackson的2.x版本中可以通过设置而使其不输出null的字段。

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有所差距。

package serialize.json;import static org.junit.Assert.*;import java.util.ArrayList;import java.util.List;import org.junit.Before;import org.junit.Test;import com.google.gson.Gson;import com.google.gson.JsonSyntaxException;import serialize.UserVo;public class GsonTest{    private UserVo user = null;    @Before    public void init()    {        user = new UserVo();        user.setName("zzh");        user.setAge(18);        UserVo f1 = new UserVo();        f1.setName("jj");        f1.setAge(17);        UserVo f2 = new UserVo();        f2.setName("qq");        f2.setAge(19);        List
friends = new ArrayList
(); friends.add(f1); friends.add(f2); user.setFriends(friends); } @Test public void writeJson() { try { String str = Gson.class.newInstance().toJson(user);//一行就可以搞定!!! System.out.println(str); System.out.println(str.length()); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } } @Test public void readJson() { String serString = "{\"name\":\"zzh\",\"age\":18,\"friends\":[{\"name\":\"jj\",\"age\":17},{\"name\":\"qq\",\"age\":19}]}"; try { UserVo userVo = Gson.class.newInstance().fromJson(serString, UserVo.class); System.out.println(userVo.getName()); } catch (JsonSyntaxException | InstantiationException | IllegalAccessException e) { e.printStackTrace(); } }}

  序列化大小:81.

Gson和Jackson的区别是:如果你的应用经常会处理大的JSON文件,那么Jackson应该是你的菜。GSON在大文件上表现得相当吃力。如果你主要是处理小文件请求,比如某个微服务或者分布式架构的初始化,那么GSON当是首选。Jackson在小文件上的表现则不如人意。

3. 阿里巴巴的FastJson

  Fastjson是一个Java语言编写的高性能的JSON处理器,由阿里巴巴公司开发。无依赖,不需要例外额外的jar,能够直接跑在JDK上。

FastJson在复杂类型的Bean转换Json上会出现一些问题,可能会出现引用的类型,导致Json转换出错,需要制定引用。FastJson采用独创的算法,将parse的速度提升到极致,超过所有json库。

package serialize.json;import static org.junit.Assert.*;import java.util.ArrayList;import java.util.List;import org.junit.Before;import org.junit.Test;import com.alibaba.fastjson.JSON;import serialize.UserVo;public class FastJsonTest{    private UserVo user = null;    @Before    public void init()    {        user = new UserVo();        user.setName("zzh");        user.setAge(18);        UserVo f1 = new UserVo();        f1.setName("jj");        f1.setAge(17);        UserVo f2 = new UserVo();        f2.setName("qq");        f2.setAge(19);        List
friends = new ArrayList
(); friends.add(f1); friends.add(f2); user.setFriends(friends); } @Test public void writeJson() { String str = JSON.toJSONString(user); System.out.println(str); System.out.println(str.length()); } @Test public void readJson() { String serString = "{\"name\":\"zzh\",\"age\":18,\"friends\":[{\"name\":\"jj\",\"age\":17},{\"name\":\"qq\",\"age\":19}]}"; UserVo userVo = JSON.parseObject(serString,UserVo.class); System.out.println(userVo.getName()); }}

注:如果只是功能要求,没有性能要求,可以使用google的Gson,如果有性能上面的要求可以使用Gson将bean转换json确保数据的正确,使用FastJson将Json转换Bean。


Google工具protoBuf

  protocol buffers 是google内部得一种传输协议,目前项目已经开源。它定义了一种紧凑得可扩展得二进制协议格式,适合网络传输,并且针对多个语言有不同得版本可供选择。

  protoBuf优点:1.性能好,效率高;2.代码生成机制,数据解析类自动生成;3.支持向前兼容和向后兼容;4.支持多种编程语言;5.字节数很小,适合网络传输节省io。缺点:1.应用不够广;2.二进制格式导致可读性差;3.缺乏自描述;
  protoBuf是需要编译工具的,这里用的是window的系统。需要下载proto.exe和protobuf-java-2.4.1.jar;
  将proto.exe放在当前工程目录下,然后编辑.proto文件,命名为UserVo.proto,如下所示:

package serialize.protobuf;option java_package = "serialize.protobuf";option java_outer_classname="UserVoProtos";message UserVo{    optional string name = 1;    optional int32 age = 2;    repeated serialize.protobuf.UserVo friends = 3;}

  在命令行中利用protoc工具生成builder类:

这里写图片描述
  看到生成了UserVoProtos.java,由于这个java文件有1千行左右,篇幅限制不便罗列。
  序列化和反序列化测试代码:

package serialize.protobuf;import org.junit.Before;import org.junit.Test;import com.google.protobuf.InvalidProtocolBufferException;public class ProtoBufTest{    UserVoProtos.UserVo.Builder user = null;    @Before public void init()    {        user = UserVoProtos.UserVo.newBuilder();        user.setName("zzh");        user.setAge(18);        UserVoProtos.UserVo.Builder f1 = UserVoProtos.UserVo.newBuilder();        f1.setName("jj");        f1.setAge(17);        UserVoProtos.UserVo.Builder f2 = UserVoProtos.UserVo.newBuilder();        f2.setName("qq");        f2.setAge(19);        user.addFriends(f1);        user.addFriends(f2);    }    @Test public void doSeri()    {        UserVoProtos.UserVo vo = user.build();        byte[] v = vo.toByteArray();        for(byte b:v)        {            System.out.printf("%02X ",b);        }        System.out.println();        System.out.println(v.length);    }    @Test public void doDeSeri()    {        byte[] v = new byte[]{
0x0A, 0x03, 0x7A, 0x7A, 0x68, 0x10, 0x12, 0x1A, 0x06, 0x0A, 0x02, 0x6A, 0x6A, 0x10, 0x11, 0x1A, 0x06, 0x0A, 0x02, 0x71, 0x71, 0x10, 0x13}; try { UserVoProtos.UserVo uvo = UserVoProtos.UserVo.parseFrom(v); System.out.println(uvo.getName()); } catch (InvalidProtocolBufferException e) { e.printStackTrace(); } }}

  序列化大小:23.

  工作机制:proto文件是对数据的一个描述,包括字段名称,类型,字节中的位置。protoc工具读取proto文件生成对应builder代码的类库。protoc xxxxx –java_out=xxxxxx 生成java类库。builder类根据自己的算法把数据序列化成字节流,或者把字节流根据反射的原理反序列化成对象。官方的示例:。proto文件中的字段类型和java中的对应关系:详见:.

.proto Type java Type c++ Type
double double double
float float float
int32 int int32
int64 long int64
uint32 int uint32
unint64 long uint64
sint32 int int32
sint64 long int64
fixed32 int uint32
fixed64 long uint64
sfixed32 int int32
sfixed64 long int64
bool boolean bool
string String string
bytes byte string

  

注:protobuf的一个缺点是需要数据结构的预编译过程,首先要编写.proto格式的配置文件,再通过protobuf提供的工具生成各种语言响应的代码。由于java具有反射和动态代码生成的能力,这个预编译过程不是必须的,可以在代码执行时来实现。有个protostuff()已经实现了这个功能。protostuff基于Google protobuf,但是提供了更多的功能和更简易的用法。其中,protostuff-runtime实现了无需预编译对java bean进行protobuf序列化/反序列化的能力。protostuff-runtime的局限是序列化前需预先传入schema,反序列化不负责对象的创建只负责复制,因而必须提供默认构造函数。此外,protostuff还可以按照protobuf的配置序列化成json/yaml/xml等格式。这里不做详述,有兴趣的朋友可以参考相关资料。

总结:

方式 优点 缺点
JSON 跨语言、格式清晰一目了然 字节数比较大,需要第三方类库
Object Serialize java原生方法不依赖外部类库 字节数大,不能跨语言
Google protobuf 跨语言、字节数比较少 编写.proto配置用protoc工具生成对应的代码
你可能感兴趣的文章
android性能监测工具,帮了我很大的忙
查看>>
linux如何分大于2T的磁盘分区
查看>>
Android:ListView、BaseAdapter、convertView、ListView优化及事件、notifyDataSetChanged()
查看>>
JS操作JSON总结
查看>>
Grafan5.4.2+Zabbix安装配置
查看>>
windows8.1 下硬盘安装 ubuntu-14.04.2-desktop-amd64.iso
查看>>
人大MapReduce编程题解答-001,A+B Per Line
查看>>
Java程序员从笨鸟到菜鸟之(三十四)大话设计模式(五)创建者模式和原型模式...
查看>>
eclipse构建maven的web项目
查看>>
Ubuntu去掉命令行前用户名和主机名步骤
查看>>
java实现断点续传
查看>>
Linux中sendmail启动速度慢的解决方法
查看>>
如何在Eclipse中查看Android API源码以及support包源码
查看>>
android SharedPreferences apply和commit的区别
查看>>
实现Bootstrap导航条可点击和鼠标悬停显示下拉菜单
查看>>
MongoDB入门3——更新文档和修改器1
查看>>
《现代操作系统》读书笔记之——进程间通信2
查看>>
面对高考
查看>>
php-excel-reader - 简单的excel 读取类
查看>>
ubuntu 在lnmp中php安装GD库
查看>>