浅析若干Java序列化工具

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

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

本文章所需要的序列化jar包都可以下载:http://download.csdn.net/detail/u013256816/9439971

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

package serialize;
import java.util.List;

public class UserVo
{
    private String name;
    private int age;
    private List<UserVo> 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<UserVo> friends = new ArrayList<UserVo>();
friends.add(f1);
friends.add(f2);
user.setFriends(friends);

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

  这里简单说明一下java序列化所占用字节大小,具体可以参考http://blog.csdn.net/u013256816/article/details/50474678

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

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库。

 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,如下所示:

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类根据自己的算法把数据序列化成字节流,或者把字节流根据反射的原理反序列化成对象。官方的示例: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工具生成对应的代码
时间: 2024-09-20 05:32:22

浅析若干Java序列化工具的相关文章

Java 序列化的高级认识

引言 将 Java 对象序列化为二进制文件的 Java 序列化技术是 Java 系列技术中一个较为重要的技术点,在大部分情况下,开发人员只需要了解被序列化的类需要实现 Serializable 接口,使用 ObjectInputStream 和 ObjectOutputStream 进行对象的读写.然而在有些情况下,光知道这些还远远不够,文章列举了笔者遇到的一些真实情境,它们与 Java 序列化相关,通过分析情境出现的原因,使读者轻松牢记 Java 序列化中的一些高级认识. ----------

Java序列化与反序列化

1.什么是序列化?为什么要序列化? Java 序列化就是指将对象转换为字节序列的过程,而反序列化则是只将字节序列转换成目标对象的过程. 我们都知道,在进行浏览器访问的时候,我们看到的文本.图片.音频.视频等都是通过二进制序列进行传输的,那么如果我们需要将Java对象进行传输的时候,是不是也应该先将对象进行序列化?答案是肯定的,我们需要先将Java对象进行序列化,然后通过网络,IO进行传输,当到达目的地之后,再进行反序列化获取到我们想要的对象,最后完成通信. 2.如何实现序列化 2.1.使用到JD

Java 序列化笔记

1. 什么是Java对象序列化     Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,即,这些对象的生命周期不会比JVM的生命周期更长.但在现实应用中,就可能要求在JVM停止运行之后能够保存(持久化)指定的对象,并在将来重新读取被保存的对象.Java对象序列化就能够帮助我们实现该功能.     使用Java对象序列化,在保存对象时,会把其状态保存为一组字节,在未来,再将这些字节组装成对象.必须注意地是,对象序列化保存的是对象的"状

Microsoft APP-V 4.5使用经验之序列化工具

在使用序列化工具时,为了简化管理.方便以后增加或删除序列化后的软件,需要注意以下问题: (1)为每个软件安装在一个单独的文件夹中,并且文件夹的长度符合8.3的规则,例如,可以将Office 2007安装在Q盘的Office.2k7文件夹中,将Office 2003安装在Q盘的Office.2k3文件夹中,将WPS 2007安装在wps2007文件夹中. (2)安装的时候,要选择"自定义安装",选择安装路径(Q盘),并且选择好要使用的产品,例如,对于Office企业版来说,可以选择安装W

关于Java常用工具您不知道的5件事

Java 常用工具,如解析.计时和声音 很多年前,当我还是高中生的时候,我曾考虑以小说作家作为我的职业追求,我订阅了一本 Writer's Digest 杂志.我记得其中有篇专 栏文章,是关于 "太小而难以保存的线头",专栏作者描述厨房储物抽屉中放满了无法分类的玩意儿.这句话我一直铭记在心,它正好用 来描述本文的内容,本系列的最后一篇(至少目前是这样). Java 平台就充满了这样的 "线头" - 有用的命令行工具和库,大多数 Java 开发人员甚至都不知道,更别提

常用Java Profiling工具的分析与比较

在 Java 程序的开发过程中,不可避免地会遇到内存使用.性能瓶颈等问题.Java Profiler 工具能帮助开发人员快速.有效地定位这些问题,因此成为了 Java 开发过程中的一个重要工具.目前市场上的 Java Profiler 工具种类繁多,本文将对目前比较常见的几种工具进行简要介绍,并从功能.性能等角度作比较,从而帮助 Java 程序员选择合适的 Java Profiler 工具. 本文主要分为三个部分:第一部分简要介绍 Java Profiler 工具的原理:第二部分对目前常见的 J

java序列化的控制

正如大家看到的那样,默认的序列化机制并不难操纵.然而,假若有特殊要求又该怎么办呢?我们可能有特殊的安全问题,不希望对象的某一部分序列化:或者某一个子对象完全不必序列化,因为对象恢复以后,那一部分需要重新创建. 此时,通过实现Externalizable接口,用它代替Serializable接口,便可控制序列化的具体过程.这个Externalizable接口扩展了Serializable,并增添了两个方法:writeExternal()和readExternal().在序列化和重新装配的过程中,会

Java序列化的机制和原理

有关Java对象的序列化和反序列化也算是Java基础的一部分,下面对Java序列化的机制和原理进行一些介绍. Java 序列化算法透析 Serialization(序列化)是一种将对象以一连串的字节描述的过程:反序列化deserialization是一种将这些字节重建成一个对象的过程.Java序列化API提供一种处理对象序列化的标准机制.在这里你能学到如何序列化一个对象,什么时候需要序列化以及Java序列化的算法,我们用一个实例来示范序列化以后的字节是如何描述一个对象的信息的. 序列化的必要性

Eclipse 3.2 Java开发工具的新特性

Eclipse是一个流行的针对Java编程的集成开发环境(IDE).它还可以用作编写其他语言(比如C++和Ruby)的环境,合并各种种类工具的框架,以及创建桌面或服务器应用程序的富客户端平台.如今,Eclipse开源社区拥有数十个项目,其范围从商务智能到社会网络等各个方面.Eclipse同时也是管理这些项目的非赢利性组织的名称.(而且,尽管我相当肯定它并非地板蜡,还是存在Eclipse汽车.足球队和口香糖.) Eclipse version 3.2在Eclipse Callisto 发行历史上具