`
my_corner
  • 浏览: 83475 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

对象序列化的几种方式的比较

阅读更多
 在java中socket传输数据时,数据类型往往比较难选择。可能要考虑带宽、跨语言、版本的兼容等问题。比较常见的做法有两种:一是把对象包装成JSON字符串传输,二是采用java对象的序列化和反序列化。随着Google工具protoBuf的开源,protobuf也是个不错的选择。对JSON,Object Serialize,ProtoBuf 做个对比。

定义一个待传输的对象UserVo:

public class UserVo{
	private String name;
	private int age;
	private long phone;
	
	private List<UserVo> friends;
……
}

 初始化UserVo的实例src:

UserVo src = new UserVo();
src.setName("Yaoming");
src.setAge(30);
src.setPhone(13789878978L);
	
UserVo f1 = new UserVo();
f1.setName("tmac");
f1.setAge(32);
f1.setPhone(138999898989L);
UserVo f2 = new UserVo();
f2.setName("liuwei");
f2.setAge(29);
f2.setPhone(138999899989L);
		
List<UserVo> friends = new ArrayList<UserVo>();
friends.add(f1);
friends.add(f2);
src.setFriends(friends);

JSON格式

采用Google的gson-2.2.2.jar 进行转义

Gson gson = new Gson();
String json = gson.toJson(src);

 得到的字符串:

{"name":"Yaoming","age":30,"phone":13789878978,"friends":[{"name":"tmac","age":32,"phone":138999898989},{"name":"liuwei","age":29,"phone":138999899989}]}

 字节数为153

Json的优点:明文结构一目了然,可以跨语言,属性的增加减少对解析端影响较小。缺点:字节数过多,依赖于不同的第三方类库。

 

Object Serialize

UserVo实现Serializalbe接口,提供唯一的版本号:

public class UserVo implements Serializable{

	private static final long serialVersionUID = -5726374138698742258L;
	private String name;
	private int age;
	private long phone;
	
	private List<UserVo> friends;

 

序列化方法:

ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(bos);
os.writeObject(src);
os.flush();
os.close();
byte[] b = bos.toByteArray();
bos.close();

 字节数是238

 

反序列化:

ObjectInputStream ois = new ObjectInputStream(fis);
vo = (UserVo) ois.readObject();
ois.close();
fis.close();

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

对象在进行序列化和反序列化的时候,必须实现Serializable接口,但并不强制声明唯一的serialVersionUID

是否声明serialVersionUID对于对象序列化的向上向下的兼容性有很大的影响。我们来做个测试:

 
思路一

把UserVo中的serialVersionUID去掉,序列化保存。反序列化的时候,增加或减少个字段,看是否成功。

public class UserVo implements Serializable{
	private String name;
	private int age;
	private long phone;
	
	private List<UserVo> friends;

 

保存到文件中:

ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(bos);
os.writeObject(src);
os.flush();
os.close();
byte[] b = bos.toByteArray();
bos.close();

FileOutputStream fos = new FileOutputStream(dataFile);
fos.write(b);
fos.close();

 

增加或者减少字段后,从文件中读出来,反序列化:

FileInputStream fis = new FileInputStream(dataFile);
ObjectInputStream ois = new ObjectInputStream(fis);
vo = (UserVo) ois.readObject();
ois.close();
fis.close();

 

结果:抛出异常信息

Exception in thread "main" java.io.InvalidClassException: serialize.obj.UserVo; local class incompatible: stream classdesc serialVersionUID = 3305402508581390189, local class serialVersionUID = 7174371419787432394
	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:560)
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1582)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1495)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1731)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
	at serialize.obj.ObjectSerialize.read(ObjectSerialize.java:74)
	at serialize.obj.ObjectSerialize.main(ObjectSerialize.java:27)
 
思路二

eclipse指定生成一个serialVersionUID,序列化保存,修改字段后反序列化

略去代码

结果:反序列化成功

结论

如果没有明确指定serialVersionUID,序列化的时候会根据字段和特定的算法生成一个serialVersionUID,当属性有变化时这个id发生了变化,所以反序列化的时候就会失败。抛出“本地classd的唯一id和流中class的唯一id不匹配”。

 

jdk文档关于serialVersionUID的描述:

写道
如果可序列化类未显式声明 serialVersionUID,则序列化运行时将基于该类的各个方面计算该类的默认 serialVersionUID 值,如“Java(TM) 对象序列化规范”中所述。不过,强烈建议 所有可序列化类都显式声明 serialVersionUID 值,原因是计算默认的 serialVersionUID 对类的详细信息具有较高的敏感性,根据编译器实现的不同可能千差万别,这样在反序列化过程中可能会导致意外的 InvalidClassException。因此,为保证 serialVersionUID 值跨不同 java 编译器实现的一致性,序列化类必须声明一个明确的 serialVersionUID 值。还强烈建议使用 private 修饰符显示声明 serialVersionUID(如果可能),原因是这种声明仅应用于直接声明类 -- serialVersionUID 字段作为继承成员没有用处。数组类不能声明一个明确的 serialVersionUID,因此它们总是具有默认的计算值,但是数组类没有匹配 serialVersionUID 值的要求。

 

Google ProtoBuf

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

以protobuf-2.5.0rc1为例,准备工作:

下载源码,解压,编译,安装

tar zxvf protobuf-2.5.0rc1.tar.gz
./configure
./make
./make install

 测试:

MacBook-Air:~ ming$ protoc --version
libprotoc 2.5.0

 安装成功!进入源码得java目录,用mvn工具编译生成所需得jar包,protobuf-java-2.5.0rc1.jar

 

1、编写.proto文件,命名UserVo.proto 

package serialize;

option java_package = "serialize";
option java_outer_classname="UserVoProtos";

message UserVo{
	optional string name = 1;
	optional int32 age = 2;
	optional int64 phone = 3;
	repeated serialize.UserVo friends = 4;
}

 

2、在命令行利用protoc 工具生成builder类

protoc -IPATH=.proto文件所在得目录 --java_out=java文件的输出路径  .proto的名称 

 得到UserVoProtos类

 

3、编写序列化代码

UserVoProtos.UserVo.Builder builder = UserVoProtos.UserVo.newBuilder();
builder.setName("Yaoming");
builder.setAge(30);
builder.setPhone(13789878978L);
		
UserVoProtos.UserVo.Builder builder1 = UserVoProtos.UserVo.newBuilder();
builder1.setName("tmac");
builder1.setAge(32);
builder1.setPhone(138999898989L);
		
UserVoProtos.UserVo.Builder builder2 = UserVoProtos.UserVo.newBuilder();
builder2.setName("liuwei");
builder2.setAge(29);
builder2.setPhone(138999899989L);
		
builder.addFriends(builder1);
builder.addFriends(builder2);
		
UserVoProtos.UserVo vo = builder.build();
		
byte[] v = vo.toByteArray();

 字节数53

 

4、反序列化

UserVoProtos.UserVo uvo = UserVoProtos.UserVo.parseFrom(dstb);
System.out.println(uvo.getFriends(0).getName());
 结果:tmac,反序列化成功

google protobuf 优点:字节数很小,适合网络传输节省io,跨语言 。缺点:需要依赖于工具生成代码。

 

工作机制

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
字段属性的描述:
写道
required: a well-formed message must have exactly one of this field.
optional: a well-formed message can have zero or one of this field (but not more than one).
repeated: this field can be repeated any number of times (including zero) in a well-formed message. The order of the repeated values will be preserved.
 
protobuf 在序列化和反序列化的时候,是依赖于.proto文件生成的builder类完成,字段的变化如果不表现在.proto文件中就不会影响反序列化,比较适合字段变化的情况。做个测试:
把UserVo序列化到文件中:
UserVoProtos.UserVo vo = builder.build();
byte[] v = vo.toByteArray();
FileOutputStream fos = new FileOutputStream(dataFile);
fos.write(vo.toByteArray());
fos.close();
 
为UserVo增加字段,对应的.proto文件:
package serialize;

option java_package = "serialize";
option java_outer_classname="UserVoProtos";

message UserVo{
	optional string name = 1;
	optional int32 age = 2;
	optional int64 phone = 3;
	repeated serialize.UserVo friends = 4;
	optional string address = 5;
}
 
从文件中反序列化回来:
FileInputStream fis = new FileInputStream(dataFile);
byte[] dstb = new byte[fis.available()];
for(int i=0;i<dstb.length;i++){
	dstb[i] = (byte)fis.read();
}
fis.close();
UserVoProtos.UserVo uvo = UserVoProtos.UserVo.parseFrom(dstb);
System.out.println(uvo.getFriends(0).getName());
 成功得到结果。
三种方式对比传输同样的数据,google protobuf只有53个字节是最少的。结论:
方式 优点 缺点
JSON

跨语言、格式清晰一目了然

字节数比较大,需要第三方类库
Object Serialize java原生方法不依赖外部类库 字节数比较大,不能跨语言
Google protobuf

跨语言、字节数比较少

编写.proto配置用protoc工具生成对应的代码

 

以上测试用例覆盖面比较窄,可能无法正确反应真实情况仅代表个人观点,欢迎随时指正和讨论。

分享到:
评论

相关推荐

    C#对象序列化与反序列化

    (1).NET支持对象序列化的几种方式 二进制序列化:对象序列化之后是二进制形式的,通过BinaryFormatter类来实现的,这个类位于System.Runtime.Serialization.Formatters.Binary命名空间下。 SOAP序列化:对象序列化...

    c#对象反序列化与对象序列化示例详解

    (1).NET支持对象序列化的几种方式二进制序列化:对象序列化之后是二进制形式的,通过BinaryFormatter类来实现的,这个类位于System.Runtime.Serialization.Formatters.Binary命名空间下。SOAP序列化:对象序列化之后...

    Hprose与.NET中各种序列化方式的对比

    本文对比了Hprose、.NET BinaryFormatter、SoapFormatter、DataContractSerializer、DataContractJsonSerializer、NetDataContractSerializer这几种序列化方式。测试程序较长,后面以附件方式来提供。

    Java高级程序设计实战教程第五章-Java序列化机制.pptx

    对象序列化是Java编程中的必备武器 Java高级程序设计实战教程第五章-Java序列化机制全文共15页,当前为第4页。 5.2.2 序列化应用 当你想把内存中的对象状态保存到一个文件中或者数据库中时候; 当你想用套接字在...

    浅谈C# 序列化与反序列化几种格式的转换

    这里介绍了几种方式之间的序列化与反序列化之间的转换 首先介绍的如何序列化,将object对象序列化常见的两种方式即string和xml对象; 第一种将object转换为string对象,这种比较简单没有什么可谈的; public string ...

    深入分析Java的序列化与反序列化

    序列化是一种对象持久化的手段。普遍应用在网络传输、RMI等场景中。本文通过分析ArrayList的序列化来介绍Java序列化的相关内容。主要涉及到以下几个问题:  怎么实现Java的序列化  为什么实现了java.io....

    几种边缘检测算子比较 (2).docx

    。。。

    C#中实现Json序列化与反序列化的几种方式

    C#中实现Json的序列化与反序列化也算是个老话题,那么在这篇文章中我们将老话重提,本文中将会学到如何使用C#,来序列化对象成为Json格式的数据,以及如何反序列化Json数据到对象。有需要的朋友们可以参考借鉴,下面...

    详解django的serializer序列化model几种方法

    序列化是将对象状态转换为可保持或传输的格式的过程。这篇文章主要介绍了详解django的serializer序列化model几种方法。具有一定的参考价值,感兴趣的小伙伴们可以参考一下

    c#序列化详解示例

    几种序列化技术:1)二进制序列化保持类型保真度,这对于在应用程序的不同调用之间保留对象的状态很有用。例如,通过将对象序列化到剪贴板,可在不同的应用程序之间共享对象。您可以将对象序列化到流、磁盘、内存和...

    JedisSerialization:实现利用Jedis向Redis中写入和得到Java对象的几种序列化工具:JDK、XML、JSON、Protostuff

    JedisSerialization实现利用Jedis向Redis中写入和得到Java对象的几种序列化工具:JDK、XML、JSON、Protostuff

    C# 对象持久化详解

    本文介绍的是除数据库之外的几种对象持久化方式。 具体如下: 保存成文本:即将内存对象以字节流的方式保存到文本中。 序列化成Xml:即将对象以Xml的格式存储。 序列化成Json:即将对象序列化成Json对象,然后存储...

    jQuey将序列化对象在前台显示地实现代码(方法总结)

    本文给大家分享jQuey将序列化对象在前台显示地的几种方式,非常不错,具有参考借鉴价值,需要的朋友一起看看吧

    C#中Serializable序列化实例详解

    本文实例讲述了C#中Serializable序列化。分享给大家供大家参考。...例如,通过将对象序列化到剪贴板,可在不同的应用程序之间共享对象。您可以将对象序列化到流、磁盘、内存和网络等等。远程处理使用序列化

    java面向对象之JVM创建及分配策略方法详解.docx

    在Java中我们有几种方式可以创建一个新的对象呢?总共有以下几种方式: new关键字 反射 clone 反序列化 Unsafe.allocateInstance 为了便于说明和理解,下文仅针对new出来的对象进行讨论。

    protolite:快速简单的对象序列化到协议缓冲区

    目前支持序列化 R 对象的“rexp.proto”、二进制 geojson 的“geobuf.proto”和矢量切片的“mvt.proto”。 该包使用 protobuf-compiler 自动生成的 C++ 代码,因此在编译时优化了整个序列化。 另一方面,...

    C#编程总结(一)序列化总结

    例如,通过将对象序列化到剪贴板,可在不同的应用程序之间共享对象。您可以将对象序列化到流、磁盘、内存和网络等等。远程处理使用序列化“通过值”在计算机或应用程序域之间传递对象。 2)XML 序列化仅序列化公共...

    .NET中JSON的序列化和反序列化的几种方式

    JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立 于编程语言的文本格式来存储和表示数据。简洁和清晰的层次...

Global site tag (gtag.js) - Google Analytics