记录序列化 - Java 啜饮
Billy Korando,2021 年 10 月 21 日了解记录序列化...
Java 16 中引入的记录 (JEP 395)解决了与序列化相关的几个关键问题。这是 Java 生态系统中经常遇到的难题的根源。
透明数据载体
记录被设计为透明的数据载体。这是通过对记录施加一些约束来实现的,包括
- 无法扩展,超类始终为
java.lang.Record
- 无法抽象,并且隐式为
final
- 无法声明实例字段或包含实例初始化器
因此,这使得记录的序列化和反序列化过程变得更加简单,并且完全由记录类的公共定义决定
- 只有记录的组件可以被序列化:
record Range(int high, int low)
- 记录序列化就像调用
defaultWriteObject
一样 - 通过调用公共规范构造函数反序列化记录
writeObject
、readObject
、readObjectNoData
、writeExternal
、readExternal
和serialVersionUID
方法和字段被忽略
这与 Stuart Marks 在此处描述的类的序列化和反序列化过程形成了鲜明的对比。
在反序列化过程中尊重封装
在反序列化类时,Java 不会调用类的构造函数。这可能会导致创建“不可能的对象”,即无法通过正常的编程路径创建的对象,如下面 Range
的示例所示
public class Range implements Serializable{
int low;
int high;
public Range(int low, int high) {
if(low > high) {
throw new IllegalArgumentException(
String.format("high: %d must be greater than low: %d", low, high));
}
this.low = low;
this.high = high;
}
}
Range
的构造函数检查字段 low
必须小于字段 high
。但是,当反序列化违反该要求的数据时,如下面的示例所示
Range [low=10, high=1]
反序列化过程成功,并使用以下值创建了一个新的 Range
实例
Range [low=10, high=1]
如果运行相同的场景,但 Range
被构建为如下所示的记录
public record Range(int low, int high)
implements Serializable {
public Range(int low, int high) {
if(low > high) {
throw new IllegalArgumentException(
String.format("high: %d must be greater than low: %d", high, low));
}
this.low = low;
this.high = high;
}
}
相反,由于 Range
的构造函数抛出了异常 (IllegalArgumentException
),因此在反序列化过程中将抛出 InvalidObjectException
Exception in thread "main" java.io.InvalidObjectException: high: 1 must be greater than low: 10
更好的模型版本控制支持
正如所有开发人员所知,变化是我们领域的一个常数,它适用于我们对领域概念建模的方式。在这里,记录再次比标准类提供了更好的模型版本控制支持。
双向兼容性
经常会向模型添加、更改和删除字段。记录在发生这种情况时提供了更好的支持。
新字段
如果向记录类添加新字段,例如向 Range
添加字段 mid
,如下例所示
public record Range(int lo, int hi, int mid) implements Serializable {}
当反序列化不包含 mid
的 Range
版本时,如下所示
Range [low=1, high=10]
JVM 会自动为类型或基元注入一个默认值,在本例中为 0
,并将其注入到规范构造函数中
Range [low=1, high=10, mid=0]
已删除和无法识别的字段
如果更改*或删除字段,则只会传递与记录组件匹配的流中的值。
因此,反序列化包含 mid
值的 Range
实例
Range [low=10, high=10, mid=5]
到不包含 mid
字段的 Range
版本
public record Range(int low, int high) implements Serializable {}
在反序列化过程中将忽略 mid
的值,并将创建包含这些值的 Range
实例
Range [low=1, high=10]
进一步阅读
编码愉快!