//TODO 只是简单使用,没有深入分析,有时间再写一篇深入分析文章

什么是序列化?

Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,就是说当JVM关闭时,我们创建的这些对象就随之丢失了。但在现实应用中,就可能要求在JVM停止运行之后能够持久化保存指定的对象,并在将来重新读取被保存的对象,比如说保存在硬盘或数据库中。Java对象序列化就能够帮助我们实现该功能。必须注意地是,对象序列化保存的是对象的”状态”,即它的成员变量。由此可知,对象序列化不会关注类中的静态变量。

使用Java对象序列化,在保存对象时,会把其状态保存为一组字节,在未来,再将这些字节组装成对象。

除了在持久化对象时会用到对象序列化之外,当使用RMI(远程方法调用),或在网络中传递对象时,都会用到对象序列化。


为什么需要序列化?

  • 持久化保存对象状态,比如保存到硬盘或者数据库
  • 网络传输

序列化的方法?

本文总结了三种方法

  • Java自带Serializable
  • JSON
  • Protobuf

一、Java自带Serializable

将需要序列化的类实现Serializable接口就可以了,Serializable接口中没有任何方法,可以理解为一个标记,即表明这个类可以序列化.

如果我们想要序列化一个对象,首先要创建某些OutputStream(如FileOutputStream、ByteArrayOutputStream等),然后将这些OutputStream封装在一个ObjectOutputStream中。这时候,只需要调用writeObject()方法就可以将对象序列化,并将其发送给OutputStream(记住:对象的序列化是基于字节的,不能使用Reader和Writer等基于字符的层次结构)。而反序列的过程(即将一个序列还原成为一个对象),需要将一个InputStream(如FileInputstream、ByteArrayInputStream等)封装在ObjectInputStream内,然后调用readObject()即可。

首先新建一个User的实体类,并实现Serializable接口

public class User implements Serializable {

    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "name=" + name + ",age=" + age;
    }
}

序列化

@Test
public void SerializeTest() {
    // 初始化
    User user = new User();
    user.setName("crankz");
    user.setAge(23);
    System.out.println("初始化:" + user);
    try {
        FileOutputStream fileOut = new FileOutputStream("user.ser");
        ObjectOutputStream out = new ObjectOutputStream(fileOut);
        out.writeObject(user);
        out.close();
        fileOut.close();
        System.out.println("序列化成功,文件保存在user.ser中");
    } catch (IOException e) {
        e.printStackTrace();
    }
}

序列化成功后,会在项目目录下生成一个文件

JAVA 序列化 JASON java 序列化存储_JSON

记事本打开看一下,都是乱码,说明是被Java序列化之后的二进制文件

JAVA 序列化 JASON java 序列化存储_Serializable_02

反序列化

@Test
public void deSerializeTest() {
    try {
        FileInputStream fileIn = new FileInputStream("user.ser");
        ObjectInputStream in = new ObjectInputStream(fileIn);
        User user = (User) in.readObject();
        in.close();
        fileIn.close();
        System.out.println("反序列化成功:" + user);
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

反序列化成功后的结果

JAVA 序列化 JASON java 序列化存储_JSON_03

二、JSON序列化

JSON常见序列化工具

  1. Jackson
  2. Fastjson
  3. Gson

还是序列化上面的User类为例,介绍三种JSON工具的常见用法。

都需要Maven添加依赖,具体去这里搜索:https://mvnrepository.com/

/**
 * Jackson的用法
 * @throws IOException
 */
@Test
public void jacksonTest() throws IOException {
    User user1 = new User();
    user1.setName("crankz");
    user1.setAge(23);

    ObjectMapper mapper = new ObjectMapper();
    //Java类转JSON
    String json = mapper.writeValueAsString(user1);
    System.out.println(json);

    //JSON转Java类
    User user2 = mapper.readValue(json, User.class);
    System.out.println(user2);
}
/**
 * Fastjson的用法
 */
@Test
public void jsonTest() {
    User user1 = new User();
    user1.setName("crankz");
    user1.setAge(23);

    //Java类转JSON
    String json = JSON.toJSONString(user1);
    System.out.println("User类转JSON:" + json);

    //JSON转Java类
    User user2 = JSON.parseObject(json, User.class);
    System.out.println(user2);
}
/**
 * Gson的用法
 */
@Test
public void gsonTest() {
    User user1 = new User();
    user1.setName("crankz");
    user1.setAge(23);

    Gson gson = new Gson();
    //Java类转JSON
    String json = gson.toJson(user1);
    System.out.println(json);

    //JSON转Java类
    User user2 = gson.fromJson(json, User.class);
    System.out.println(user2);
}

三、Protobuf序列化

使用步骤

  1. 定义Protobuf结构
  2. 使用protoc生成相应java类,并导入到实际项目中
  3. java代码使用

1.定义Protobuf结构

Protobuf要根据他的规则定义结构,这里只做简单演示,具体请看官网:https://github.com/google/protobuf

JAVA 序列化 JASON java 序列化存储_JSON_04

2.使用protoc生成相应java类,并导入到实际项目中

官网下载相应的工具:https://github.com/google/protobuf/releases

下载相应平台版本,我这里下载window版本。

JAVA 序列化 JASON java 序列化存储_序列化_05

然后执行命令

protoc.exe --java_out=输出目录 目标文件

JAVA 序列化 JASON java 序列化存储_JAVA 序列化 JASON_06

执行成功后会在输出目录多一个java格式的文件

JAVA 序列化 JASON java 序列化存储_JAVA 序列化 JASON_07

把UserPt.java导入到Java项目中

3.Java代码使用Protobuf

请自行添加Maven依赖,去这里:https://mvnrepository.com/

这里只做简单演示,详细请看:https://github.com/google/protobuf

/**
 * Protobuf的使用
 * @throws IOException
 */
@Test
public void protoTest() throws IOException {
    //按照定义的数据结构,创建一个User类
    UserPt.UserProto.Builder userBuilder =
            UserPt.UserProto.newBuilder();
    userBuilder.setName("crankz");
    userBuilder.setAge(23);
    UserPt.UserProto user1 = userBuilder.build();

    //将数据写到输出流
    ByteArrayOutputStream output = new ByteArrayOutputStream();
    user1.writeTo(output);

    //上面是发送方,将数据序列化后发送
    byte[] byteArray = output.toByteArray();
    //下面是接受放,将接受的数据反序列化

    //接受到流并读取
    ByteArrayInputStream input = new ByteArrayInputStream(byteArray);

    //反序列化
    UserPt.UserProto user2 = UserPt.UserProto.parseFrom(input);
    System.out.println("user2_name:" + user2.getName());
    System.out.println("user2_age:" + user2.getAge());
}

三种方法简单总结:

方式

优点

缺点

JSON

跨语言、格式清晰、便于人理解

字节数比较大,需要第三方类库

Object Serialize

java原生方法不依赖外部类库

字节数大,不能跨语言

Google protobuf

跨语言、字节数比较少

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

参考:

http://www.hollischuang.com/archives/1150