序列化的几种方式
来源:互联网 发布:软件ui设计 编辑:程序博客网 时间:2024/04/25 13:08
定义一个待传输的对象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的描述:
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());
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++ Typedouble double doublefloat float floatint32 int int32int64 long int64uint32 int uint32unint64 long uint64sint32 int int32sint64 long int64fixed32 int uint32fixed64 long uint64sfixed32 int int32sfixed64 long int64bool boolean boolstring String stringbytes byte stringoptional: 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.
- UserVoProtos.UserVo vo = builder.build();
- byte[] v = vo.toByteArray();
- FileOutputStream fos = new FileOutputStream(dataFile);
- fos.write(vo.toByteArray());
- fos.close();
- 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());
跨语言、格式清晰一目了然
字节数比较大,需要第三方类库Object Serializejava原生方法不依赖外部类库字节数比较大,不能跨语言Google protobuf跨语言、字节数比较少
编写.proto配置用protoc工具生成对应的代码
以上测试用例覆盖面比较窄,可能无法正确反应真实情况仅代表个人观点,欢迎随时指正和讨论。
- 序列化的几种方式
- 序列化的几种方式
- Java序列化的几种方式
- 序列化的几种方式
- Java序列化的几种方式
- Java序列化的几种方式
- 序列化的几种方式
- Java序列化的几种方式
- Java序列化的几种方式
- 序列化的几种方式
- 对象序列化的几种方式的比较
- 序列化反序列化的几种方式
- GSON序列化时排除字段的几种方式
- spring redis 几种序列化方式的比较
- 几种Java序列化方式的实现
- Java序列化的几种方式以及序列化的作用(顶)
- Java序列化的几种方式以及序列化的作用
- Java序列化的几种方式以及序列化的作用
- 记一次Apach DBUtils的使用中出现的错误报错 Incorrect syntax near ','.
- Android ImageView的scaleType属性
- 各种Java序列化性能比较
- pcb文件的作用
- POJ 3233 Matrix Power Series
- 序列化的几种方式
- PL/SQL编程
- Matalab 中如何用一个cell存储多个字符串并将其一次性写入EXcel中
- Oracle 用户概念与基本操作
- Ardrone2 ROS Image和OpenCV Image相互转化
- 线程池
- 妙用PPT 2003刻录多媒体光盘
- JavaScript中创建对象的几种方法
- 点击类别,并通过Toast将类别的名字显示出来