Java序列化

  • 序列化:将数据结构或对象转换成可以存储或传输的形式,通常是二进制字节流,也可以是JSON、XML等文本格式。

  • 反序列化:将序列化后的数据重新转换成原来的对象或数据结构。

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

  • 序列化对应于TCP/IP4层模型中的应用层。
    Java序列化对应于TCPIP4层模型中的应用层

1. Java中的序列化

  • Java自带的序列化只需实现java.io.Serializable接口即可

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    @Builder
    @ToString
    public class RpcRequest implements Serializable {
    private static final long serialVersionUID = 1905122041950251207L;
    private String requestId;
    private String interfaceName;
    private String methodName;
    private Object[] parameters;
    private Class<?>[] paramTypes;
    private RpcMessageTypeEnum rpcMessageTypeEnum;
    }
  • serialVersionUID:序列化版本号,用于验证序列化和反序列化的兼容性。如果类的serialVersionUID不匹配,反序列化时会抛出InvalidClassException异常。建议在每个可序列化的类中显式声明serialVersionUID,以确保版本控制的一致性。

  • serialVersionUID为什么被static修饰了还会被“序列化”:

    • 通常情况下,static变量是属于类的,不属于对象实例,所以不会被序列化。所以serialVersionUID本身作为static变量确实不作为对象状态被序列化。
    • 但是,serialVersionUID的值被Java序列化机制特殊处理了——作为一个版本标识符被读取并写入序列化流中,用于在反序列化时进行版本兼容性检查。
  • transient关键字:被该关键字修饰的变量不会被序列化,当对象被反序列化时,被其修饰的变量值不会被持久化和恢复。需要注意的是:

    • transient关键字只能修饰变量,不能修饰类和方法
    • transient修饰的变量在反序列化后会被赋予默认值(如null0false等),而不是原来的值
    • static变量因为不属于任何对象,所以无论是否有transient修饰,都不会被序列化
  • Java序列化的缺点:

    • 不支持跨语言调用
    • 性能差
    • 存在安全问题

2. 其他序列化方式

  • 比较常用的序列化协议有 Hessian、Kryo、Protobuf、ProtoStuff,这些都是基于二进制的序列化协议。详见Java 序列化详解