序列化及方式对比分析

什么是序列化和反序列化?

如果需要持久化Java对象比如将Java对象保存在文件中, 或在网络传输Java对象, 这些场景需要使用到序列化.

序列化

将数据结构或对象转化成二进制字节流的过程

反序列化

将在序列化过程中所产生的二进制字节流转换成数据结构或者对象的过程

目的

序列化的主要目的是 通过网络传输对象 或者是将 对象存储到文件系统,数据库或内存 中.

常见序列化协议

JDK自带的序列化方式,因为序列化效率低而且存在安全问题, 一般不会使用.

像JSON和XML这种属于文本类序列化方式, 虽然可读性好, 但是性能较差, 一般不会选择

常用的基于二进制序列化协议有: Hessian, Kryo, Protobuf, ProtoStuff

JDK 自带的序列化方式

JDK自带的序列化, 只需要实现java.io.Serializable接口即可, 如下所示:

/**
 * @author 花木凋零成兰
 * @title SerializableTest
 * @date 2024/4/26 16:41
 * @package com.example.demo.demo1
 * @description 测试JDK的序列化方式
 */
public class SerializableTest implements Serializable {
    private static final long serialVersionUID = 1L;
    private String filed1;
    private String[] filed2;
    private Object parameters;
}

serialVersionUID有什么作用呢?

主要用于版本控制, 反序列化时, 会检查serialVersionUID是否符合与当前类的serialVersionUID一致, 不一致则会抛出InvalidClassException异常

推荐每个序列化类都手动指定其serialVersionUID, 如果不手动指定, 则编译器会动态生成默认的serialVersionUID

serialVersionUID不是被static修饰了吗? 为什么还会序列化?

static修饰的变量是静态变量, 位于方法区, 本身不会被序列化, 但是serialVersionUID的序列化做了特殊处理, 序列化时, 会将其序列化到二进制字节流中, 在反序列化时也会解析并做一致性判断

即, serialVersionUID只是用来被JVM识别, 实际并没有被序列化

有些字段并不想序列化怎么办?

对于不想序列化的字段, 可以使用transient关键字修饰; 其作为为: 阻止实例中用此关键字修饰的变量序列化, 对象被反序列化时, 被该关键字修饰的变量值不会被持久化和恢复

关于transient注意:

  • 只能修饰变量, 不能修饰类和方法
  • 修饰的变量, 在被反序列化后变量值会被置成变量类型的默认值, 例如,修饰int类型, 那么反序列化后结果就是0
  • static变量,因为不属于任何对象, 所以无论有无transient关键字修饰, 都不会被序列化

为什么不推荐使用JDK序列化

  • 不支持跨语言调用
  • 性能差; 序列化后的字节数组体积较大, 传输成本加大
  • 存在安全问题

Kryo

是一个高性能的序列化/反序列化工具, 由于其变长存储特性并使用了字节码生成机制, 拥有较高的运行速度和较小的字节码体积

另外, Kryo序列化实现趋于成熟, 且在多个著名开源项目中广泛使用

github地址

使用示例如下:

/**
 * @author 花木凋零成兰
 * @title HelloKryo
 * @date 2024/4/26 17:14
 * @package com.example.demo.demo1
 * @description 尝试Kryo序列化
 */
public class HelloKryo {
    static public void main (String[] args) throws Exception {
        // 创建Kryo实例
        Kryo kryo = new Kryo();
        // 使用register方法注册一个类SomeClass 让Kryo知道应该序列化的类
        kryo.register(SomeClass.class);
        // 创建SomeClass实例
        SomeClass object = new SomeClass();
        object.value = "Hello Kryo!";
        // 创建文件路径对象 且文件名为file.bin
        Path path = Paths.get("file.bin");
        // 创建输出对象 用于写入序列化数据
        // Files.newOutputStream(path) 打开输出流 用于写入指定路径的文件
        Output output = new Output(Files.newOutputStream(path));
        // 将object序列化后的二进制数据 写入到output流中
        kryo.writeObject(output, object);
        // 关闭output流
        output.close();
        // 创建输入对象 读取序列化数据
        Input input = new Input(Files.newInputStream(path));
        // 将读取的二进制数据 反序列化为SomeClass对象实例
        SomeClass object2 = kryo.readObject(input, SomeClass.class);
        System.out.println(object2.value);
        // 关闭输入读取流
        input.close();
    }
    static public class SomeClass {
        String value;
    }
}

Protobuf

出自于Google, 性能比较优秀, 支持多种语言和跨平台, 但是使用过于繁琐, 需要自定义 LDL 文件和生成对应的序列化代码, 不够灵活, 但是也保证了没有序列化漏洞的风险

github地址

ProtoStuff

ProtoStuff 基于 Google Protobuf, 提供了更多的功能和更简易的用法, 同时性能也很优秀

github地址

Hessian

是轻量级,自定义描述的二进制RPC协议, 支持跨语言, 但是是比较老的序列化实现

总结

Kryo是专门针对Java语言序列化方式其性能非常好,所以推荐使用Kryo来作为序列化方式,

参考

https://github.com/Snailclimb/guide-rpc-framework

  • 36
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值