23

我正在阅读Effective 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;
    }
}

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

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()

4

4 回答 4

22

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

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

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

private void readObjectNoData() throws ObjectStreamException;

每个可序列化类都可以定义自己的 readObjectNoData 方法。如果可序列化的类没有定义 readObjectNoData 方法,那么在上面列出的情况下,类的字段将被初始化为其默认值(如 The JavaTM Language Specification, Second Edition 的第 4.5.5 节中所列);当引入对 readObjectNoData 方法的支持时,此行为与 JavaTM 2 SDK 标准版 1.4 版之前的 ObjectInputStream 一致。如果一个可序列化的类确实定义了一个 readObjectNoData 方法并且出现了上述情况,

于 2011-09-16T13:29:02.553 回答
3

我创建的 Person 类中是否有任何不变量?他们什么时候会被侵犯?

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

我在类中复制了readObjectData()方法的代码Employee,但它从未被调用。readObject()什么时候调用该方法?

序列化没有readObjectData()涉及任何方法,这一定是一个错字。readObject()每次反序列化序列化对象时都会调用该方法。

在反序列化包含该方法的类的子类时,该readObjectNoData()方法会因某些晦涩的极端情况而受到影响。

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

(更新)

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

参考的文字是:

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

所以这可能发生在两种情况下:

  • 解码对象流的 JVM 具有正在反序列化的子类的更新版本 ( Employee),它扩展了某些父类 ( Person)。最初 *en* 编码对象流的 JVM 具有这些类的不同的旧版本,其中Person还不是Employee.
  • 有人故意弄乱对象流以破坏事物。
于 2011-09-16T13:25:27.517 回答
3

“可扩展”的意思是“可以有一个子类”。

readObjectNoData 用于不寻常的情况,其中序列化器(编写器)正在使用没有基类的类的版本,而类的反序列化器(读取器)具有基于子类的类版本。子类可以通过实现 readObjectNoData 说“如果我的基类不在序列化数据中也没关系 - 只需创建一个空的”。请参阅这些发行说明

于 2011-09-16T13:32:30.630 回答
0

您的 Person 类中的不变量可能类似于:

age must be greater than 0

因此,当使用默认值 0 实例化 Person 类时(请参见此处:http: //download.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html),您将违反该不变量。

但是,给定默认构造函数,你会没事的:)

于 2011-09-16T13:24:32.627 回答