Java:在序列化过程中何时添加 readObjectNoData()?

2022-09-01 23:26:50

我正在阅读有效Java中的序列化章节。我试图理解下面的段落,这是在书中找到的。

如果实现的类具有可序列化和可扩展的实例字段,则应注意一点。如果类具有在将类的实例字段初始化为其默认值(整数类型为零,布尔值为 false,对象引用类型为 null)时会违反的不变量,则必须将此 readObjectNoData 方法添加到类中:

// readObjectNoData for stateful extendable serializable classes
private void readObjectNoData() throws InvalidObjectException {
    throw new InvalidObjectException("Stream data required");
}

我不确定这句话是什么意思。

为了测试这一点,我创建了一个名为Person的类(可序列化和可扩展)

class Person implements Serializable {

    private String name;
    private int age;

    Person() {
        this("default", 1);
    }
    
    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

– 和一个类:员工,它扩展了它。

class Employee extends Person {

    String address;

    public Employee() {
        super();
        address = "default_address";
    }

    public Employee(String name, int age, String address) {
        super(name, age);
        this.address = address;
    }
}

我创建的 Person 类中是否有任何不变量?它们何时会被违反?我在 Employee 类中复制粘贴了该方法的代码,但它从未被调用过。何时调用该方法?我错过了什么吗?readObjectData()readObject()


答案 1

Java 对象序列化规范中的 readObjectNoData 部分似乎很有趣(见下文)。

您对问题的编辑给出了一个完美的例子。如果 是 当它没有扩展时, 后来当它扩展时, 那么该部分将被初始化为空字符串和 0 age。使用此方法,可以分别将它们初始化为“name”和 1。EmployeeserializedPersondeserializedPerson

对于可序列化的对象,readObjectNoData 方法允许类在子类实例被反序列化并且序列化流未将相关类列为反序列化对象的超类时控制其自身字段的初始化。如果接收方使用与发送方不同的反序列化实例类版本,并且接收方的版本扩展了发送方版本未扩展的类,则可能会发生这种情况。如果序列化流已被篡改,也可能发生这种情况。因此,readObjectNoData 对于正确初始化反序列化的对象非常有用,尽管源流存在“恶意”或不完整的源流。

private void readObjectNoData() throws ObjectStreamException;

每个可序列化的类都可以定义自己的 readObjectNoData 方法。如果可序列化类未定义 readObjectNoData 方法,则在上面列出的情况下,该类的字段将初始化为其默认值(如 JavaTM 语言规范第二版第 4.5.5 节所述);此行为与 JavaTM 2 SDK 标准版 1.4 版之前的 ObjectInputStream 的行为一致,当时引入了对 readObjectNoData 方法的支持。如果可序列化的类确实定义了一个 readObjectNoData 方法,并且出现了上述条件,则 readObjectNoData 将在反序列化期间调用,否则如果流将有问题的类列为被反序列化的实例的超类,则将调用类定义的 readObject 方法。


答案 2

我创建的 Person 类中是否有任何不变量?它们何时会被违反?

没有明确,但想象一下,类中的其他方法假设它永远不会,并且如果它曾经是,就会抛出。在这种情况下,的非空性是一个不变量。namenullNullPointerExceptionname

我复制了类中方法的代码,但它从未被调用。何时调用该方法?readObjectData()EmployeereadObject()

序列化不涉及任何方法,这必须是拼写错误。每次反序列化对象时都会调用该方法。readObjectData()readObject()

在反序列化包含该方法的类的子类时,对于某些不起眼的角落情况,将命中该方法。readObjectNoData()

SunOracle 网站上的高级序列化文章介绍了这些序列化帮助程序方法的用途。我建议你从那里开始,并发布你可能遇到的任何后续问题。

(更新)

如果您感到好奇,在 1.4 版中添加了 readObjectNoData 方法,以涵盖涉及将可序列化超类添加到现有可序列化类的极端情况。有关详细信息,请参阅序列化规范序列化 3.5

引用的文本是:

对于可序列化的对象,readObjectNoData 方法允许类在子类实例被反序列化并且序列化流未将相关类列为反序列化对象的超类时控制其自身字段的初始化。如果接收方使用与发送方不同的反序列化实例类版本,并且接收方的版本扩展了发送方版本未扩展的类,则可能会发生这种情况。如果序列化流已被篡改,也可能发生这种情况。因此,readObjectNoData 对于正确初始化反序列化的对象非常有用,尽管源流存在“恶意”或不完整的源流。

因此,这可能在两种情况下发生:

  • 解码对象流的 JVM 具有正在反序列化的子类的较新版本 (),该子类扩展了某个父类 ()。最初 *en* 编码对象流的 JVM 具有这些类的不同旧版本,其中尚未成为 的超类。EmployeePersonPersonEmployee
  • 有人故意弄乱了对象流,以便破坏事物。