序列化的概念

序列化:把对象转化为字节序列的过程。

反过来说就是反序列化。

序列化的应用


1、储存对象,可以是永久的储存在硬盘的一个文件上,也可以是储存在redis支持序列化存储的容器中。


2、网络上远程传输对象。



Rpc之间调用以流字节的方式传输,java序列化是java平台上的序列化工具,一个对象需要在网络上传输,需实现 Serializable接口



@Data
@AllArgsConstructor
public class Pojo implements Serializable {

    public String name;
    public Integer age;
    public Address address; //  transient 不被序列化

    public static void main(String[] args) throws IOException, ClassNotFoundException {

        Address address = new Address("zj", "hz");
        Pojo pojo = new Pojo("van", 22, address);
        // 序列化到磁盘Io
        try (FileOutputStream fos = new FileOutputStream("pojo");
             ObjectOutputStream oos = new ObjectOutputStream(fos)) {
            oos.writeObject(pojo);
            oos.flush();
        }

        try (FileInputStream fis = new FileInputStream("pojo");
             ObjectInputStream ois = new ObjectInputStream(fis);
        ) {
            Pojo p = (Pojo) ois.readObject();
            System.out.println(p);
        }


    }



}




@Data
@AllArgsConstructor
public  class Address implements Serializable {
    public String province;
    public String city;
}





以上代码会把对象序列化,存在磁盘上


内容为�� sr !org.spafka.serializable.java.Pojo����FH�� L addresst &Lorg/spafka/serializable/java/Address;L aget Ljava/lang/Integer;L namet Ljava/lang/String;xpsr $org.spafka.serializable.java.AddressۦX%o�� L cityq ~ L provinceq ~ xpt hzt zjsr java.lang.Integer⠤���8 I valuexr java.lang.Number��� ���  xp   t van



大致看得懂



对序列化过程做基准测试


Address address = new Address("zj", "hz");
Pojo pojo = new Pojo("van", 22, address);


long start = System.currentTimeMillis();
IntStream.rangeClosed(0, 1000000).forEach((i) -> {
    byte[] bytes = null;
    try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
         ObjectOutputStream oos = new ObjectOutputStream(baos);) {

        oos.writeObject(pojo); //335个字节长度
        bytes = baos.toByteArray();
    } catch (IOException e) {
        e.printStackTrace();
    }

    //     System.out.println(bytes.length);
    try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
         ObjectInputStream ois = new ObjectInputStream(bais);) {
        Pojo p = (Pojo) ois.readObject();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
});

System.out.println(System.currentTimeMillis() - start);  //15630




一百万的对象的序列化反序列化花了15s,每个序列化后的大小为335个字节  对比其他序列化方式,不得不说有点弱。




protobuf是谷歌开发的一套序列化工具


谷歌出品,必属精品。



在java上开发protostuff 很容易


添加依赖包



<dependency>
    <groupId>com.dyuproject.protostuff</groupId>
    <artifactId>protostuff-core</artifactId>
    <version>1.1.2</version>
</dependency>
<dependency>
    <groupId>com.dyuproject.protostuff</groupId>
    <artifactId>protostuff-runtime</artifactId>
    <version>1.1.2</version>
</dependency>
<dependency>
    <groupId>org.objenesis</groupId>
    <artifactId>objenesis</artifactId>
    <version>2.6</version>
</dependency>





调用起来也十分方便


public class ProtuStuffUtil {

    private static volatile Map <Class <?>, Schema <?>> cachedSchema = new ConcurrentHashMap <>();

    //schema 对象的描述信息,  构建schema的过程可能会比较耗时,因此希望使用过的类对应的schema能被缓存起来。代码如下,不再赘述:
    private static <T> Schema <T> getSchema(Class <T> cls) {
        Schema <T> schema = (Schema <T>) cachedSchema.get(cls);
        if (schema == null) {
            schema = RuntimeSchema.createFrom(cls);
            synchronized (ProtuStuffUtil.class) {
                if (schema != null) {
                    cachedSchema.put(cls, schema);
                }
            }
        }
        return schema;
    }

    private static Objenesis objenesis = new ObjenesisStd(true);

    //    第3行:获得对象的类;
//    第4行:使用LinkedBuffer分配一块默认大小的buffer空间;
//    第6行:通过对象的类构建对应的schema;
//    第7行:使用给定的schema将对象序列化为一个byte数组,并返回。
    @SuppressWarnings("unchecked")
    public static <T> byte[] serialize(T obj) {
        Class <T> cls = (Class <T>) obj.getClass();
        LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try {
            Schema <T> schema = getSchema(cls);
            return ProtostuffIOUtil.toByteArray(obj, schema, buffer);
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        } finally {
            buffer.clear();
        }
    }

    //
//    第3行:使用objenesis实例化一个类的对象;
//    第4行:通过对象的类构建对应的schema;
//    第5,6行:使用给定的schema将byte数组和对象合并,并返回。
    public static <T> T deserialize(byte[] data, Class <T> cls) {
        try {
            T message = objenesis.newInstance(cls);
            Schema <T> schema = getSchema(cls);
            ProtostuffIOUtil.mergeFrom(data, message, schema);
            return message;
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }


}





对protostuff做基准测试



@Test
public void benchMarkProtusuff() {

    long start = System.currentTimeMillis();

    Address address = new Address("zj", "hz");
    Pojo pojo = new Pojo("van", 22, address);


    IntStream.rangeClosed(0, 1000000).forEach((i) -> {
        byte[] serialize = ProtuStuffUtil.serialize(pojo);
        //  System.out.println(serialize.length); //17个字节长度
        Pojo deserialize = ProtuStuffUtil.deserialize(serialize, Pojo.class);
    });
    System.out.println(System.currentTimeMillis() - start); //629

}