我正在阅读 Effective Java 中的序列化一章。
谁调用了readObject()和writeObject()?为什么这些方法被声明为私有?
以下是本书的一段代码
// StringList with a reasonable custom serialized form
public final class StringList implements Serializable {
private transient int size = 0;
private transient Entry head = null;
//Other code
private void writeObject(ObjectOutputStream s)
throws IOException {
s.defaultWriteObject();
s.writeInt(size);
// Write out all elements in the proper order.
for (Entry e = head; e != null; e = e.next)
s.writeObject(e.data);
}
}
}
是否有任何特定原因将变量size
声明为瞬态,然后在writeObject方法中明确写入它?如果它没有被声明为瞬态,那么无论如何都会写出来,对吗?
答案 0 :(得分:34)
(1)方法未在任何类或接口中声明。一个实现Serializable
接口且需要special special handling during the serialization and deserialization process must implement这些方法的类和序列化器/反序列化器将尝试反映这些方法。
这是Java中相当奇怪的角落之一,其中API实际上是在javaDoc中定义的......但如果方法已在接口中定义,那么它们有< / em>为public
(我们无法通过添加private
修饰符来实现锁定它的接口方法。)
为什么是私有 - javaDoc没有给出提示。也许它们被指定为为私有,因为除了实现者之外没有其他类可以使用它们。根据定义,它们私有。
(2)该示例仅显示特殊处理的方式。在此示例中,size
是暂时的,不会被序列化。但是现在我们引入了特殊处理程序,这个处理程序将size
的值添加到流中。与非瞬态字段的常规方法的不同之处可能是结果流中元素的顺序(如果它很重要......)。
如果瞬态字段是在超类中定义的,并且子类想要序列化该值,那么这个例子就有意义了。
答案 1 :(得分:17)
除不应被错误方使用之外,以下是这些方法隐私的另一个原因:
我们不希望子类重写这些方法。相反,每个类都可以有自己的writeObject
方法,序列化引擎会一个接一个地调用它们。这只能通过私有方法实现(这些方法不会被覆盖)。 (同样适用于readObject
。)
(请注意,这仅适用于本身实现Serializable的超类。)
这样,子类和超类可以独立发展,并且仍然与旧版本的存储对象保持兼容。
答案 2 :(得分:10)
关于readObject()/ writeObject()是私有的,这里是交易:如果你的类Bar扩展了一些类Foo; Foo还实现了readObject()/ writeObject(),Bar也实现了readObject()/ writeObject()。
现在,当一个Bar对象被序列化或反序列化时,JVM需要自动为Foo和Bar调用readObject()/ writeObject()(即不需要显式地对这些超类方法进行分类)。但是,如果这些方法除了私有之外,它变成了方法覆盖,JVM不再能够调用子类对象上的超类方法。
因此他们必须是私人的!
答案 3 :(得分:7)
readObject
和writeObject
由Object(Input/Output)Stream
类调用。
这些方法是(并且必须)声明为私有(在实现自己的时),证明/表明实现不会继承和覆盖或重载这两种方法。这里的技巧是JVM自动检查是否在相应的方法调用期间声明了任一方法。请注意,JVM可以在需要时调用类的私有方法,但没有其他对象可以。因此,保持了类的完整性,并且序列化协议可以继续正常工作。
关于瞬态int
,它只是控制整个对象序列化的序列化。但请注意,从技术上讲,如果所有字段都是短暂的,则调用defaultWriteObject()
甚至不是必要。但我认为仍然建议为灵活性目的调用它,以便以后可以在类中引入非瞬态成员,从而保持兼容性。
答案 4 :(得分:0)
关于瞬态变量,理解我们为什么要声明瞬态变量以及稍后在writeobject方法中序列化它们的最佳方法是检查/分析/调试LinkedList / HashMap / etc类的readobject / writeobject方法。
这通常在您希望以预定义的顺序序列化/反序列化类变量而不依赖于默认行为/顺序时完成。
答案 5 :(得分:0)
假设您有一个引用Socket的A类。如果要序列化A类对象,则不能直接因为Socket不是Serializable。在这种情况下,您可以编写如下代码。
public class A implements implements Serializable {
// mark Socket as transient so that A can be serialized
private transient Socket socket;
private void writeObject(ObjectOutputStream out)throws IOException {
out.defaultWriteObject();
// take out ip address and port write them to out stream
InetAddress inetAddress = socket.getInetAddress();
int port = socket.getPort();
out.writeObject(inetAddress);
out.writeObject(port);
}
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException{
in.defaultReadObject();
// read the ip address and port form the stream and create a frest socket.
InetAddress inetAddress = (InetAddress) in.readObject();
int port = in.readInt();
socket = new Socket(inetAddress, port);
}
}
忽略任何与网络相关的问题,因为目的是显示writeObject / readObject方法的使用。