浅析若干Java序列化工具【转】
在Java中socket传输数据时,数据类型往往比较难选择。可能要考虑带宽、跨语言、版本的兼容等问题。比较常见的做法有:
- 采用java对象的序列化和反序列化
- 把对象包装成JSON字符串传输
- Google工具protoBuf的开源
本文章所需要的序列化jar包都可以下载:http://download.csdn.net/detail/u013256816/9439971。
为了便于说明各个做法的区别,分别对这三种做法进行阐述。 对UserVo对象进行序列化,class UserVo如下:
|
1
2
3
4
5
6
7
8
9
10
|
package serialize;import java.util.List;public class UserVo{ private String name; private int age; private List<UserVo> friends; //此处省略Getter和Setter方法} |
初始化一个UserVo实例:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
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<UserVo> friends = new ArrayList<UserVo>();friends.add(f1);friends.add(f2);user.setFriends(friends); |
采用java对象的序列化和反序列化
这里简单说明一下java序列化所占用字节大小,具体可以参考http://blog.csdn.net/u013256816/article/details/50474678。
|
1
2
3
4
5
6
|
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格式。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
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<UserVo> friends = new ArrayList<UserVo>(); 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有所差距。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
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<UserVo> friends = new ArrayList<UserVo>(); 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库。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
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<UserVo> friends = new ArrayList<UserVo>(); 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,如下所示:
|
1
2
3
4
5
6
7
8
9
10
11
|
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千行左右,篇幅限制不便罗列。
序列化和反序列化测试代码:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
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类根据自己的算法把数据序列化成字节流,或者把字节流根据反射的原理反序列化成对象。官方的示例:https://developers.google.com/protocol-buffers/docs/javatutorial。proto文件中的字段类型和java中的对应关系:详见:https://developers.google.com/protocol-buffers/docs/proto.
| .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(http://code.google.com/p/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工具生成对应的代码 |
浅析若干Java序列化工具【转】的更多相关文章
- Java 序列化工具类
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sun.misc.BASE64Decoder; import sun.m ...
- Java 序列化对象工具类
SerializationUtils.java package javax.utils; import java.io.ByteArrayInputStream; import java.io.Byt ...
- 浅析JAVA序列化
1.简述 Serialization(序列化) 是一种将对象以一连串的字节描述的过程:反序列化deserialization是一种将这些字节重建成一个对象的过程. 在分布式环境中,经常需要将Objec ...
- Java 序列化与反序列化
1.什么是序列化?为什么要序列化? Java 序列化就是指将对象转换为字节序列的过程,而反序列化则是只将字节序列转换成目标对象的过程. 我们都知道,在进行浏览器访问的时候,我们看到的文本.图片.音频. ...
- Java 序列化Serializable详解
Java 序列化Serializable详解(附详细例子) Java 序列化Serializable详解(附详细例子) 1.什么是序列化和反序列化Serialization(序列化)是一种将对象以一连 ...
- 简述java序列化
1. 什么是Java对象序列化 Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,即,这些对象的生命周期不会比JVM的生命周期 ...
- Java序列化的几种方式以及序列化的作用
Java序列化的几种方式以及序列化的作用 本文着重讲解一下Java序列化的相关内容. 如果对Java序列化感兴趣的同学可以研究一下. 一.Java序列化的作用 有的时候我们想要把一个Java对象 ...
- Java序列化技术与Protobuff
http://www.cnblogs.com/fangfan/p/4094175.html http://www.cnblogs.com/fangfan/p/4094175.html 前言: Java ...
- Google FlatBuffers——开源、跨平台的新一代序列化工具
前段时间刚试用了一个序列化工具cereal,请看cereal:C++实现的开源序列化库,打算再总结下我对google proto buf序列化库的使用呢, 结果还没动手,大Google又出了一个新的. ...
随机推荐
- scala编程第17章学习笔记(3)
可变(mutable)集合与不可变(immutable)集合 为了更易于完成不可变集合到可变集合的转换,或者反向转换,Scala提供了一些语法糖.纵使不可变集和映射并不支持真正的+=方法,Scala还 ...
- GO语言基础之并发concurrency
并发Concurrency 很多人都是冲着 Go 大肆宣扬的高并发而忍不住跃跃欲试,但其实从源码的解析来看,goroutine 只是由官方实现的超级“线程池”而已.不过话说回来,每个实例 4-5KB的 ...
- 用C语言获取任意文件的长度(可能大于2GB)
用C语言获取文件长度的常见思路是: 打开文件后用 fseek() 函数把文件位置指针移动到文件的末尾,用 ftell() 获得这时位置指针距文件头的字节数,这个字节数就是文件的长度.但是这样做也会受到 ...
- DIRECT Project
http://www.healthit.gov/policy-researchers-implementers/direct-project Launched in March 2010 as a p ...
- What's the difference between - (one hyphen) and — (two hyphens) in a command?
bash中看到这样的命令, curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash - sudo apt-get install - ...
- sass与less
刚刚发现sass这个东西,前端真热闹,下面比较一下这两者的共同点与区别. 开头总结一下,方便记忆:sass依赖后端计算能力,less依赖客户端的计算能力. 很多开发者不选择LESS是因为LESS输出修 ...
- Java 实例 - 队列(Queue)用法
队列是一种特殊的线性表,它只允许在表的前端进行删除操作,而在表的后端进行插入操作. LinkedList类实现了Queue接口,因此我们可以把LinkedList当成Queue来用. 以下实例演示了队 ...
- 【转载】如何在Oracle中复制表结构和表数据
1. 复制表结构及其数据: create table table_name_new as select * from table_name_old 2. 只复制表结构: create table ta ...
- ZOJ 2320 Cracking' RSA
其次布尔线性方程组,高斯消元.这道题目的关键部分是看的神牛watashi的思路.另附上watashi的思路 我把他的java模板翻译成了C++的了...存起来以后当模板用...a[i][j]表示第i个 ...
- 灰度图像二值化-----c++实现
前天闲着没事干,就写了写BMP图像处理,感觉大家还比较感兴趣..所以现在没事,继续更新..这次简单的写了灰度图像二值化..这是什么概念呢? 图像的二值化的基本原理 图像的二值化处理就是将图像上的点的灰 ...