• Java序列化的方式。


    0、前言

    本文主要对几种常见Java序列化方式进行实现。包括Java原生以流的方法进行的序列化、Json序列化、FastJson序列化、Protobuff序列化。


    1、Java原生序列化

    Java原生序列化方法即通过Java原生流(InputStream和OutputStream之间的转化)的方式进行转化。需要注意的是JavaBean实体类必须实现Serializable接口,否则无法序列化。Java原生序列化代码示例如下所示:

     1 package serialize;
     2 
     3 import java.io.BufferedInputStream;
     4 import java.io.ByteArrayOutputStream;
     5 import java.io.IOException;
     6 import java.io.ObjectInputStream;
     7 import java.io.ObjectOutputStream;
     8 import java.util.ArrayList;
     9 import java.util.List;
    10 /**
    11  * 
    12  * @author liqqc
    13  *
    14  */
    15 public class JavaSerialize {
    16     public static void main(String[] args) throws ClassNotFoundException, IOException {
    17         new JavaSerialize().start();
    18     }
    19 
    20     public void start() throws IOException, ClassNotFoundException {
    21         User u = new User();
    22         List<User> friends = new ArrayList<>();
    23         u.setUserName("张三");
    24         u.setPassWord("123456");
    25         u.setUserInfo("张三是一个很牛逼的人");
    26         u.setFriends(friends);
    27 
    28         User f1 = new User();
    29         f1.setUserName("李四");
    30         f1.setPassWord("123456");
    31         f1.setUserInfo("李四是一个很牛逼的人");
    32 
    33         User f2 = new User();
    34         f2.setUserName("王五");
    35         f2.setPassWord("123456");
    36         f2.setUserInfo("王五是一个很牛逼的人");
    37 
    38         friends.add(f1);
    39         friends.add(f2);
    40 
    41         Long t1 = System.currentTimeMillis();
    42         ByteArrayOutputStream out = new ByteArrayOutputStream();
    43         ObjectOutputStream obj = new ObjectOutputStream(out);
    44         for(int i = 0; i<10; i++) {
    45             obj.writeObject(u);
    46         }
    47         System.out.println("java serialize: " +(System.currentTimeMillis() - t1) + "ms; 总大小:" + out.toByteArray().length );
    48 
    49         Long t2 = System.currentTimeMillis();
    50         ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new java.io.ByteArrayInputStream(out.toByteArray())));
    51         User user = (User) ois.readObject();
    52         System.out.println("java deserialize: " + (System.currentTimeMillis() - t2) + "ms; User: " + user);
    53     }
    54 
    55 }

    运行结果:

    java serialize: 8ms; 总大小:420
    java deserialize: 1ms; User: User [userId=null, userName=张三, passWord=123456, userInfo=张三是一个很牛逼的人, friends=[User [userId=null, userName=李四, passWord=123456, userInfo=李四是一个很牛逼的人, friends=null], User [userId=null, userName=王五, passWord=123456, userInfo=王五是一个很牛逼的人, friends=null]]]

    2、Json序列化

    Json序列化一般会使用jackson包,通过ObjectMapper类来进行一些操作,比如将对象转化为byte数组或者将json串转化为对象。现在的大多数公司都将json作为服务器端返回的数据格式。比如调用一个服务器接口,通常的请求为xxx.json?a=xxx&b=xxx的形式。Json序列化示例代码如下所示:

     1 package serialize;
     2 
     3 import java.io.IOException;
     4 import java.util.ArrayList;
     5 import java.util.List;
     6 
     7 import com.fasterxml.jackson.databind.ObjectMapper;
     8 /**
     9  * 
    10  * @author liqqc
    11  *
    12  */
    13 public class JsonSerialize {
    14     public static void main(String[] args) throws IOException {
    15         new JsonSerialize().start();
    16     }
    17 
    18     public void start() throws IOException {
    19         User u = new User();
    20         List<User> friends = new ArrayList<>();
    21         u.setUserName("张三");
    22         u.setPassWord("123456");
    23         u.setUserInfo("张三是一个很牛逼的人");
    24         u.setFriends(friends);
    25 
    26         User f1 = new User();
    27         f1.setUserName("李四");
    28         f1.setPassWord("123456");
    29         f1.setUserInfo("李四是一个很牛逼的人");
    30 
    31         User f2 = new User();
    32         f2.setUserName("王五");
    33         f2.setPassWord("123456");
    34         f2.setUserInfo("王五是一个很牛逼的人");
    35 
    36         friends.add(f1);
    37         friends.add(f2);
    38 
    39         ObjectMapper mapper = new ObjectMapper();
    40         Long t1 = System.currentTimeMillis();
    41         byte[] writeValueAsBytes = null;
    42         for (int i = 0; i < 10; i++) {
    43             writeValueAsBytes = mapper.writeValueAsBytes(u);
    44         }
    45         System.out.println("json serialize: " + (System.currentTimeMillis() - t1) + "ms; 总大小:" + writeValueAsBytes.length);
    46         Long t2 = System.currentTimeMillis();
    47         User user = mapper.readValue(writeValueAsBytes, User.class);
    48         System.out.println("json deserialize: " + (System.currentTimeMillis() - t2) + "ms; User: " + user);
    49 
    50     }
    51 }

    运行结果:

    json serialize: 55ms; 总大小:341
    json deserialize: 35ms; User: User [userId=null, userName=张三, passWord=123456, userInfo=张三是一个很牛逼的人, friends=[User [userId=null, userName=李四, passWord=123456, userInfo=李四是一个很牛逼的人, friends=null], User [userId=null, userName=王五, passWord=123456, userInfo=王五是一个很牛逼的人, friends=null]]]

    3、FastJson序列化

    fastjson 是由阿里巴巴开发的一个性能很好的Java 语言实现的 Json解析器和生成器。特点:速度快,测试表明fastjson具有极快的性能,超越任其他的java json parser。功能强大,完全支持java bean、集合、Map、日期、Enum,支持范型和自省。无依赖,能够直接运行在Java SE 5.0以上版本 
    支持Android。使用时候需引入FastJson第三方jar包。FastJson序列化代码示例如下所示:

     1 package serialize;
     2 
     3 import java.util.ArrayList;
     4 import java.util.List;
     5 
     6 import com.alibaba.fastjson.JSON;
     7 /**
     8  * 
     9  * @author liqqc
    10  *
    11  */
    12 public class FastJsonSerialize {
    13 
    14     public static void main(String[] args) {
    15         new FastJsonSerialize().start();
    16     }
    17 
    18     public void start(){
    19         User u = new User();
    20         List<User> friends = new ArrayList<>();
    21         u.setUserName("张三");
    22         u.setPassWord("123456");
    23         u.setUserInfo("张三是一个很牛逼的人");
    24         u.setFriends(friends);
    25 
    26         User f1 = new User();
    27         f1.setUserName("李四");
    28         f1.setPassWord("123456");
    29         f1.setUserInfo("李四是一个很牛逼的人");
    30 
    31         User f2 = new User();
    32         f2.setUserName("王五");
    33         f2.setPassWord("123456");
    34         f2.setUserInfo("王五是一个很牛逼的人");
    35 
    36         friends.add(f1);
    37         friends.add(f2);
    38 
    39         //序列化  
    40         Long t1 = System.currentTimeMillis();
    41         String text = null;
    42         for(int i = 0; i<10; i++) {
    43             text = JSON.toJSONString(u); 
    44         }
    45         System.out.println("fastJson serialize: " +(System.currentTimeMillis() - t1) + "ms; 总大小:" + text.getBytes().length);
    46         //反序列化  
    47         Long t2 = System.currentTimeMillis();
    48         User user = JSON.parseObject(text, User.class);
    49         System.out.println("fastJson serialize: " + (System.currentTimeMillis() -t2) + "ms; User: " + user);
    50     }
    51 }

    运行结果:

    fastJson serialize: 284ms; 总大小:269
    fastJson serialize: 26ms; User: User [userId=null, userName=张三, passWord=123456, userInfo=张三是一个很牛逼的人, friends=[User [userId=null, userName=李四, passWord=123456, userInfo=李四是一个很牛逼的人, friends=null], User [userId=null, userName=王五, passWord=123456, userInfo=王五是一个很牛逼的人, friends=null]]]

    4、ProtoBuff序列化

    ProtocolBuffer是一种轻便高效的结构化数据存储格式,可以用于结构化数据序列化。适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。

    优点:跨语言;序列化后数据占用空间比JSON小,JSON有一定的格式,在数据量上还有可以压缩的空间。

    缺点:它以二进制的方式存储,无法直接读取编辑,除非你有 .proto 定义,否则无法直接读出 Protobuffer的任何内容。

    其与thrift的对比:两者语法类似,都支持版本向后兼容和向前兼容,thrift侧重点是构建跨语言的可伸缩的服务,支持的语言多,同时提供了全套RPC解决方案,可以很方便的直接构建服务,不需要做太多其他的工作。 Protobuffer主要是一种序列化机制,在数据序列化上进行性能比较,Protobuffer相对较好。

    ProtoBuff序列化对象可以很大程度上将其压缩,可以大大减少数据传输大小,提高系统性能。对于大量数据的缓存,也可以提高缓存中数据存储量。原始的ProtoBuff需要自己写.proto文件,通过编译器将其转换为java文件,显得比较繁琐。百度研发的jprotobuf框架将Google原始的protobuf进行了封装,对其进行简化,仅提供序列化和反序列化方法。其实用上也比较简洁,通过对JavaBean中的字段进行注解就行,不需要撰写.proto文件和实用编译器将其生成.java文件,百度的jprotobuf都替我们做了这些事情了。

    一个带有jprotobuf注解的JavaBean如下所示,如果你想深入学习可以参照https://github.com/google/protobuf

     1 package serialize;
     2 
     3 import java.io.Serializable;
     4 import java.util.List;
     5 import com.baidu.bjf.remoting.protobuf.FieldType;
     6 import com.baidu.bjf.remoting.protobuf.annotation.Protobuf;
     7 
     8 public class User implements Serializable {
     9     private static final long serialVersionUID = -7890663945232864573L;
    10 
    11     @Protobuf(fieldType = FieldType.INT32, required = false, order = 1)
    12     private Integer userId;
    13 
    14     @Protobuf(fieldType = FieldType.STRING, required = false, order = 2)
    15     private String userName;
    16 
    17     @Protobuf(fieldType = FieldType.STRING, required = false, order = 3)
    18     private String passWord;
    19 
    20     @Protobuf(fieldType = FieldType.STRING, required = false, order = 4)
    21     private String userInfo;
    22 
    23     @Protobuf(fieldType = FieldType.OBJECT, required = false, order = 5)
    24     private List<User> friends;
    25 
    26     public Integer getUserId() {
    27         return userId;
    28     }
    29 
    30     public void setUserId(Integer userId) {
    31         this.userId = userId;
    32     }
    33 
    34     public String getUserName() {
    35         return userName;
    36     }
    37 
    38     public void setUserName(String userName) {
    39         this.userName = userName;
    40     }
    41 
    42     public String getPassWord() {
    43         return passWord;
    44     }
    45 
    46     public void setPassWord(String passWord) {
    47         this.passWord = passWord;
    48     }
    49 
    50     public String getUserInfo() {
    51         return userInfo;
    52     }
    53 
    54     public void setUserInfo(String userInfo) {
    55         this.userInfo = userInfo;
    56     }
    57 
    58     public List<User> getFriends() {
    59         return friends;
    60     }
    61 
    62     public void setFriends(List<User> friends) {
    63         this.friends = friends;
    64     }
    65 
    66     @Override
    67     public String toString() {
    68         return "User [userId=" + userId + ", userName=" + userName + ", passWord=" + passWord + ", userInfo=" + userInfo
    69                 + ", friends=" + friends + "]";
    70     }
    71 
    72 }

    jprotobuf序列化代码示例如下所示:

     1 package serialize;
     2 
     3 import java.io.IOException;
     4 import java.util.ArrayList;
     5 import java.util.List;
     6 
     7 import com.baidu.bjf.remoting.protobuf.Codec;
     8 import com.baidu.bjf.remoting.protobuf.ProtobufProxy;
     9 /**
    10  * 
    11  * @author liqqc
    12  *
    13  */
    14 public class ProtoBuffSerialize {
    15 
    16     public static void main(String[] args) throws IOException {
    17         new ProtoBuffSerialize().start();
    18     }
    19 
    20     public void start() throws IOException {
    21         Codec<User> studentClassCodec = ProtobufProxy.create(User.class, false);
    22 
    23         User u2 = new User();
    24         List<User> friends = new ArrayList<>();
    25         u2.setUserName("张三");
    26         u2.setPassWord("123456");
    27         u2.setUserInfo("张三是一个很牛逼的人");
    28         u2.setFriends(friends);
    29 
    30         User f1 = new User();
    31         f1.setUserName("李四");
    32         f1.setPassWord("123456");
    33         f1.setUserInfo("李四是一个很牛逼的人");
    34 
    35         User f2 = new User();
    36         f2.setUserName("王五");
    37         f2.setPassWord("123456");
    38         f2.setUserInfo("王五是一个很牛逼的人");
    39         friends.add(f1);
    40         friends.add(f2);
    41 
    42         Long stime_jpb_encode = System.currentTimeMillis();
    43         byte[] bytes = null;
    44         for(int i = 0; i<10; i++) {
    45             bytes = studentClassCodec.encode(u2);
    46         }
    47         System.out.println("jprotobuf序列化耗时:" + (System.currentTimeMillis() - stime_jpb_encode) + "ms; 总大小:" + bytes.length);
    48 
    49         Long stime_jpb_decode = System.currentTimeMillis();
    50         User user = studentClassCodec.decode(bytes);
    51         Long etime_jpb_decode = System.currentTimeMillis();
    52         System.out.println("jprotobuf反序列化耗时:"+ (etime_jpb_decode-stime_jpb_decode) + "ms; User: " + user);
    53     }
    54 
    55 }

    运行结果:

    jprotobuf序列化耗时:9ms; 总大小:148
    jprotobuf反序列化耗时:0ms; User: User [userId=null, userName=张三, passWord=123456, userInfo=张三是一个很牛逼的人, friends=[User [userId=null, userName=李四, passWord=123456, userInfo=李四是一个很牛逼的人, friends=null], User [userId=null, userName=王五, passWord=123456, userInfo=王五是一个很牛逼的人, friends=null]]]

    5、总结

    我们通过Main方法来进行对比测试,(但是通过测试发现少量数据无法准确显示每种序列化方式的优劣,故这里无法给出比较好的答案,仅供参考)。示例代码如下所示:

     1 package serialize;
     2 
     3 import java.io.IOException;
     4 
     5 /**
     6  * @author liqqc
     7  */
     8 public class Main {
     9 
    10     public static void main(String[] args) throws IOException, ClassNotFoundException {
    11 
    12         ProtoBuffSerialize protoBuffSerialize = new ProtoBuffSerialize();
    13         protoBuffSerialize.start();
    14 
    15         System.err.println();
    16         System.err.println();
    17 
    18         JavaSerialize javaSerialize = new JavaSerialize();
    19         javaSerialize.start();
    20         System.err.println();
    21 
    22         JsonSerialize jsonSerialize = new JsonSerialize();
    23         jsonSerialize.start();
    24         System.err.println();
    25 
    26         FastJsonSerialize fastJsonSerialize = new FastJsonSerialize();
    27         fastJsonSerialize.start();
    28     }
    29 }

    运行结果:

    jprotobuf序列化耗时:7ms; 总大小:148
    jprotobuf反序列化耗时:0ms
    
    java serialize: 6ms; 总大小:420
    java deserialize: 1ms
    
    json serialize: 37ms; 总大小:341
    json deserialize: 27ms
    
    fastJson serialize: 173ms; 总大小:269
    fastJson serialize: 35ms

    上面的测试仅供参考,并不能代表通过大量数据进行测试的结果。可以发现:序列化后对象的所占大小上:protobuff序列化所占总大小是最少的;其次是fastJson序列化;最后是json序列化和java原生序列化。对于序列化耗时,上面的测试不准。

    还是去看看专业测试分析吧,具体情况可以进去看看https://github.com/eishay/jvm-serializers/wiki

    本文仅仅简单介绍了下几种序列化方式的实现,并未经过大量测试对其进行对比分析,待后续有时间和精力在进行补充

  • 相关阅读:
    android学习笔记(一)
    中缀表达式转后缀表达式
    evernote使用推荐
    guns框架初试(一)eclipse环境配置之lombok
    guns框架初试(三)eclipse环境配置之成功运行
    guns框架初试(二)环境配置之数据库配置以及被迫修改数据库密码
    ssh 连接出错
    linux网银
    vimrepress
    gnome3自启动
  • 原文地址:https://www.cnblogs.com/hunrry/p/9183130.html
Copyright © 2020-2023  润新知