Java:何时在序列化期间添加readObjectNoData()?
我正在阅读“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 y) { this.name = name; this.age = y; } }
以及扩展它的类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()方法? 我错过了什么吗?
Java对象序列化规范中的readObjectNoData部分看起来很有趣(见下文)。
您对该问题的编辑提供了一个完美的例子。 如果Employee
在没有扩展Person
时被serialized
,并且稍后deserialized
,那么Person
部分将被初始化为空字符串和0年龄。 使用此方法,您可以将它们分别初始化为“name”和1。
对于可序列化对象,readObjectNoData方法允许类在子类实例反序列化的情况下控制其自己字段的初始化,并且序列化流不会将所讨论的类列为反序列化对象的超类。 如果接收方使用与发送方不同版本的反序列化实例的类,并且接收方的版本扩展了未由发送方版本扩展的类,则可能发生这种情况。 如果序列化流已被篡改,也可能发生这种情况; 因此,尽管存在“恶意”或不完整的源流,readObjectNoData对于正确初始化反序列化对象非常有用。
private void readObjectNoData() throws ObjectStreamException;
每个可序列化的类可以定义自己的readObjectNoData方法。 如果可序列化类没有定义readObjectNoData方法,那么在上面列出的情况下,类的字段将被初始化为它们的默认值(如JavaTM语言规范第二版第4.5.5节所列); 当引入对readObjectNoData方法的支持时,此行为与JavaTM 2 SDK,Standard Edition 1.4版之前的ObjectInputStream的行为一致。 如果可序列化的类确实定义了一个readObjectNoData方法并且出现了上述条件,则在反序列化期间将调用readObjectNoData,否则将调用类定义的readObject方法,如果该类已将该类列为流的超类正在反序列化的实例。
我创建的Person类中是否有任何不变量? 什么时候会被侵犯?
没有显式,但想象一下,类中的其他方法假定name
永远不会为null
,如果有的话会抛出NullPointerException
。 在这种情况下, name
的非null值是不变的。
我在
Employee
类中复制了readObjectData()
方法的代码,但它从未被调用过。 何时调用readObject()
方法?
没有方法readObjectData()
涉及序列化,这必须是一个错字。 每次反序列化序列化对象时都会调用readObject()
方法。
当反序列化包含该方法的类的子类时,对于某些模糊的角点案例,会readObjectNoData()
方法。
Sun Oracle网站上的高级序列化文章介绍了这些序列化帮助程序方法的用途。 我建议你从那里开始并发布你可能遇到的任何后续问题。
(更新)
如果您感到好奇,则在1.4版中添加了readObjectNoData方法,以涵盖涉及向现有可序列化类添加可序列化超类的极端情况。 详细信息可以在序列化规范Serialization,3.5中找到 。
引用的文本是:
对于可序列化对象,readObjectNoData方法允许类在子类实例反序列化的情况下控制其自己字段的初始化,并且序列化流不会将所讨论的类列为反序列化对象的超类。 如果接收方使用与发送方不同版本的反序列化实例的类,并且接收方的版本扩展了未由发送方版本扩展的类,则可能发生这种情况。 如果序列化流已被篡改,也可能发生这种情况; 因此,尽管存在“恶意”或不完整的源流,readObjectNoData对于正确初始化反序列化对象非常有用。
所以这可能发生在两种情况:
- 解码对象流的JVM有一个更新版本的子类被反序列化(
Employee
),它扩展了一些父类(Person
)。 最初编码对象流的JVM具有这些类的不同旧版本,其中Person
还不是Employee
的超类。 - 有人故意弄乱对象流以破坏事物。
“可扩展”意味着“可以有一个子类”。
readObjectNoData用于一种不常见的情况,其中序列化程序(编写器)使用的是没有基类的类的版本,而类的反序列化程序(阅读器)具有基于子类的类的版本。 通过实现readObjectNoData,子类可以说“如果我的基类不在序列化数据中就可以了 – 只需使其为空”。 请参阅这些发行说明 。
Person类中的不变量可能类似于:
age must be greater than 0
因此,当使用默认值0(参见此处: http : //download.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html )实例化Person类时,您将违反该不变量。
但是,给定默认构造函数你会没事的:)