3

这篇关于可序列化记录的帖子中指出

反序列化通过调用记录类的规范构造函数创建一个新的记录对象,将从流中反序列化的值作为参数传递给规范构造函数。这是安全的,因为这意味着记录类可以在将值分配给字段之前对其进行验证,就像普通 Java 程序通过 new 创建记录对象一样。“不可能”的物体是不可能的。

这与仅用于验证的构造函数争论。但是,当构造函数操作参数时,这会导致相当奇怪的行为。考虑这个非常人为的简单示例:

以下记录a在保存之前对其进行操作:

import java.io.Serializable;

public record TRecord (int a) implements Serializable {
    public TRecord {
        a = a-1;
    }
}

下面的程序只是第一次保存序列化的记录并在随后的时间加载它:

import java.io.*;

public class TestRecords {

    public static void main(String args[]) {
        TRecord a1 = null;

        try {
            FileInputStream fileIn = new FileInputStream("tmp");
            ObjectInputStream in = new ObjectInputStream(fileIn);
            a1 = (TRecord) in.readObject();
            in.close();
            fileIn.close();
        } catch (IOException | ClassNotFoundException i) {
            // ignore for now
        }
        if (a1 == null) {
            try {
                a1 = new TRecord(5);
                FileOutputStream fileOut = new FileOutputStream("tmp");
                ObjectOutputStream out = new ObjectOutputStream(fileOut);
                out.writeObject(a1);
                out.close();
                fileOut.close();
                System.out.printf("Serialized data is saved in /tmp/employee.ser");
            } catch (IOException i) {
                i.printStackTrace();
            }
        }

        System.out.println(a1);
    }
}

第一次运行的输出是TRecord[a=4],TRecord[a=3]在随后的运行中,我从反序列化中得到的状态与我在那里输入的不同。使用类似下面的类似课程TClass[a=4]每次都会得到相同的结果。

import java.io.Serializable;

public class TClass implements Serializable {
    private int a;

    public TClass(final int a) {
        this.a = a-1;
    }

    public int getA() {return a;}

    public String toString() {
        return "Class[" + a + "]";
    }
}

所以我的问题是:是否有任何规则禁止/不鼓励使用构造函数进行验证以外的任何操作(例如,我正在考虑在存储输入之前散列密码)?还是有另一种方法来反序列化对象以恢复初始状态?

4

1 回答 1

11

如果您查看文档以获取记录,它会显示以下内容:

对于所有记录类,以下不变量必须保持:如果记录 R 的组件是 c1、c2、...cn,那么如果记录实例被复制如下:

 R copy = new R(r.c1(), r.c2(), ..., r.cn());  

那么它必须是 r.equals(copy) 的情况。

但是,您的记录类并非如此:

jshell> TRecord r1 = new TRecord(42);
r1 ==> TRecord[a=41]

jshell> TRecord copy = new TRecord(r1.a());
copy ==> TRecord[a=40]

jshell> r1.equals(copy)
$4 ==> false

换句话说,您的记录类型违反了这个不变量,这也是您看到反序列化不一致的原因。

于 2021-05-10T21:59:43.843 回答