Java序列化案例demo(包含Kryo、JDK原生、Protobuf、ProtoStuff以及hessian)(一)

简介: Java序列化案例demo(包含Kryo、JDK原生、Protobuf、ProtoStuff以及hessian)(一)

一、Kryo序列化(优先选择)


介绍

kryo-Gihub仓库地址


Kryo 是一个高性能的序列化/反序列化工具,由于其变长存储特性并使用了字节码生成机制,拥有较高的运行速度和较小的字节码体积,并且Kryo 已经是一种非常成熟的序列化实现了,已经在 Twitter、Groupon、Yahoo 以及多个著名开源项目(如 Hive、Storm)中广泛的使用。


基于Java的快速高效的对象序列化框架,旨在提供快速、高效和易用的API。无论文件、数据库或网络数据Kryo都可以随时完成序列化。Kryo还可以执行自动深拷贝(克隆)、浅拷贝(克隆),这是对象到对象的直接拷贝,非对象→字节→对象的拷贝。支持互相引用,比如类A引用类B,类B引用类A,可以正确地反序列化。


说明:对于Kryo需要你进行提前进行手动注册class类,这种方式能够更够让序列化后的内容更小,序列化更快,当然你也可以不选择手动注册,那么内容会稍微大一些。


快速开始




<dependency>
    <groupId>com.esotericsoftware.kryo</groupId>
    <artifactId>kryo5</artifactId>
    <version>5.3.0</version>
</dependency>


pojo类:


package com.changlu.serialize.pojo;


import java.io.Serializable;


/**
 * @ClassName Message
 * @Author ChangLu
 * @Date 6/14/2022 8:39 PM
 * @Description 消息类
 */
public class Message implements Serializable {
    private int messageType;
    public int getMessageType() {
        return messageType;
    }
    public void setMessageType(int messageType) {
        this.messageType = messageType;
    }
}



package com.changlu.serialize.pojo;


import java.io.Serializable;


/**
 * @ClassName User
 * @Author ChangLu
 * @Date 6/14/2022 8:45 PM
 * @Description 用户类
 */
public class User implements Serializable {
    private String name;
    private Integer age;
    public User(){
    }
    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}



package com.changlu.serialize.pojo;


/**
 * @ClassName RPCResponse
 * @Author ChangLu
 * @Date 6/14/2022 8:39 PM
 * @Description RPC响应实体类
 */
public class RPCResponse<T> extends Message{
    private T data;
    public T getData() {
        return data;
    }
    public void setData(T data) {
        this.data = data;
    }
    @Override
    public String toString() {
        return "RPCResponse{" +
                "data=" + data +
                '}';
    }
}


序列化接口以及实现类:


package com.changlu.serialize;
public interface Serializer {
    /**
     * 序列化
     *
     * @param obj 要序列化的对象
     * @return 字节数组
     */
    byte[] serialize(Object obj);
    /**
     * 反序列化
     *
     * @param bytes 序列化后的字节数组
     * @param clazz 目标类
     * @param <T>   类的类型。举个例子,  {@code String.class} 的类型是 {@code Class<String>}.
     *              如果不知道类的类型的话,使用 {@code Class<?>}
     * @return 反序列化的对象
     */
    <T> T deserialize(byte[] bytes, Class<T> clazz);
}


package com.changlu.serialize;
import com.esotericsoftware.kryo.kryo5.Kryo;
import com.esotericsoftware.kryo.kryo5.io.Input;
import com.esotericsoftware.kryo.kryo5.io.Output;
import com.esotericsoftware.kryo.kryo5.objenesis.strategy.StdInstantiatorStrategy;
import com.esotericsoftware.kryo.kryo5.util.DefaultInstantiatorStrategy;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
 * @ClassName KryoSerializer
 * @Author ChangLu
 * @Date 6/14/2022 8:33 PM
 * @Description Kryo序列化工具
 */
public class KryoSerializer implements Serializer{
    //由于Kryo是线程不安全的,所以我们这里使用ThreadLocal来解决线程安全问题
    public static ThreadLocal<Kryo> kryoThreadLocal = ThreadLocal.withInitial(()->{
            Kryo kryo = new Kryo();
            kryo.setReferences(true);//检测循环依赖,默认值为true,避免版本变化显示设置
            //方式一:设置无需注册,那么之后就无需对需要进行序列号的类进行注册(性能略差)
            //kryo.setRegistrationRequired(false);//默认值为true,避免版本变化显示设置
            ((DefaultInstantiatorStrategy)kryo.getInstantiatorStrategy())
                    .setFallbackInstantiatorStrategy(new StdInstantiatorStrategy());//设置默认的实例化器
             //方式二:由于默认是需要进行注册的,若是不设置为false,那么就需要进行手动注册class类
            kryo.register(User.class);
            kryo.register(RPCResponse.class);
            return kryo;
    });
    @Override
    public byte[] serialize(Object obj) {
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             final Output output = new Output(baos)
        ) {
            Kryo kryo = kryoThreadLocal.get();
            //进行序列化
            kryo.writeObject(output, obj);
            kryoThreadLocal.remove();
            return output.toBytes();
        } catch (IOException e) {
            throw new RuntimeException("Serialization failed");
        }
    }
    @Override
    public <T> T deserialize(byte[] bytes, Class<T> clazz) {
        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
             Input input = new Input(byteArrayInputStream)) {
            Kryo kryo = kryoThreadLocal.get();
            Object obj = kryo.readObject(input, clazz);
            kryoThreadLocal.remove();
            return clazz.cast(obj);
        } catch (IOException e) {
            throw new RuntimeException("Serialization failed");
        }
    }
}


测试


当前Kryo采用的是注册class的方式来进行反序列化的:


package com.changlu.serialize;
import com.changlu.serialize.pojo.RPCResponse;
import com.changlu.serialize.pojo.User;
import org.junit.Test;
/**
 * @Description: 序列号测试工具
 * @Author: changlu
 * @Date: 9:31 AM
 */
public class SerializerTest {
    @Test
    public void test(){
        //测试对象
        RPCResponse<User> rpcResponse = new RPCResponse<>();
        rpcResponse.setData(new User("changlu", 123));
        //测试kryo序列化
        testSerialize(new KryoSerializer(), rpcResponse);
    }
    public <T> void testSerialize(Serializer serializer, T t) {
        System.out.println(String.format("=====开始序列化:%s=====", serializer.getClass()));
        System.out.println("开始进行序列化");
        long startTime = System.nanoTime();
        //序列化
        byte[] data = serializer.serialize(t);
        long endTime = System.nanoTime();
        System.out.println("  序列化时间为:" + (endTime - startTime) / 1000000000.0 + "秒");
        System.out.println("  序列化后的内容为:" + new String(data));
        System.out.println("  序列化后的长度为:" + data.length);
        System.out.println("开始进行反序列化");
        startTime = System.nanoTime();
        //反序列化
        System.out.println("  反序列化后得到的对象为:" + serializer.deserialize(data, t.getClass()));
        endTime = System.nanoTime();
        System.out.println("  反序列化时间为:" + (endTime - startTime) / 1000000000.0 + "秒");
        System.out.println(String.format("=====结束序列化:%s=====", serializer.getClass()) + "\n");
    }
}



二、JDK原生序列化


介绍

介绍:Java类通过实现Serializable接口来实现该类对象的序列化,这个接口非常特殊,没有任何方法,只起标识作用。Java序列化保留了对象类的元数据(如类、成员变量、继承类信息),以及对象数据等,兼容性最好,但不支持跨语言,而且性能一般。


对于serialVersionUID 的说明:序列化号 serialVersionUID 属于版本控制的作用。序列化的时候 serialVersionUID 也会被写入二级制序列,当反序列化时会检查 serialVersionUID 是否和当前类的 serialVersionUID 一致。如果 serialVersionUID 不一致则会抛出 InvalidClassException 异常。强烈推荐每个序列化类都手动指定其 serialVersionUID,如果不手动指定,那么编译器会动态生成默认的序列化号


一般不使用原生的JDK序列化:


不支持跨语言调用 : 如果调用的是其他语言开发的服务的时候就不支持了。

性能差 :相比于其他序列化框架性能更低,主要原因是序列化之后的字节数组体积较大,导致传输成本加大。


快速开始



基于目录一中的包来进行实现即可,只要实现serialize的接口,编写序列化与反序列化方法即可!


package com.changlu.serialize;
import java.io.*;
/**
 * @Description: JDK原生序列化
 * @Author: changlu
 * @Date: 9:54 AM
 */
public class JdkSerializer implements Serializer{
    @Override
    public byte[] serialize(Object obj) {
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
        ) {
            oos.writeObject(obj);
            return baos.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException("Serialization failed");
        }
    }
    @Override
    public <T> T deserialize(byte[] bytes, Class<T> clazz) {
        try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
             ObjectInputStream ois = new ObjectInputStream(bais);
        ) {
            return  (T) ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            throw new RuntimeException("Serialization failed");
        }
    }
}


测试


基于一中的测试添加一条代码即可来进行测试:




//测试JDK原生序列化工具
testSerialize(new JdkSerializer(), rpcResponse);



发现:JDK的序列化与反序列化能够比Kryo的还快,但是序列化后的大小是大了几十倍了。


相关文章
|
8天前
|
JavaScript 前端开发 Java
Java数字化产科管理系统源码,多家医院应用案例,可直接上项目
Java开发的数字化产科管理系统,已在多家医院实施,支持直接部署。系统涵盖孕产全程,包括门诊、住院、统计和移动服务,整合高危管理、智能提醒、档案追踪等功能,与HIS等系统对接。采用前后端分离架构,Java语言,Vue前端,若依框架,MySQL数据库。优势在于提升就诊效率,降低漏检率,自动报表生成,减少重复工作,支持数据研究,并实现医院与卫计委平台的数据互通,打造全生育周期健康服务。
24 4
|
15天前
|
Java 测试技术 数据库连接
解密Java事务传播行为与隔离级别:案例详解与解决方案
解密Java事务传播行为与隔离级别:案例详解与解决方案
8 1
|
3天前
|
存储 安全 Java
Java面试题:请解释Java中的泛型集合框架?以及泛型的经典应用案例
Java面试题:请解释Java中的泛型集合框架?以及泛型的经典应用案例
8 0
|
11天前
|
机器学习/深度学习 人工智能 自然语言处理
Java中的自然语言处理应用案例分析
Java中的自然语言处理应用案例分析
|
11天前
|
负载均衡 Java 微服务
Java中的可扩展微服务架构设计案例解析
Java中的可扩展微服务架构设计案例解析
|
16天前
|
JSON Java 数据安全/隐私保护
一篇文章讲明白Java第三方支付接入案例(支付宝)
一篇文章讲明白Java第三方支付接入案例(支付宝)
12 0
|
16天前
|
JSON Java 数据安全/隐私保护
一篇文章讲明白Java第三方支付接入案例(支付宝)
一篇文章讲明白Java第三方支付接入案例(支付宝)
16 0
|
17天前
|
Java BI Serverless
Java8 Stream深度解析:30个案例3万字助你精通集合筛选、归约、分组与聚合操作
Java8 Stream深度解析:30个案例3万字助你精通集合筛选、归约、分组与聚合操作
|
17天前
|
设计模式 算法 Java
Java中的设计模式:实战案例分享
Java中的设计模式:实战案例分享
|
19天前
|
Java
java的lambda延时执行特性案例
java的lambda延时执行特性案例
9 0