为什么readObject和writeObject是私有的,为什么我会明确地写出瞬态变量?

时间:2011-09-19 06:42:59

标签: java serialization effective-java

我正在阅读 Effective Java 中的序列化一章。

  1. 谁调用了readObject()和writeObject()?为什么这些方法被声明为私有?

  2. 以下是本书的一段代码

    // 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方法中明确写入它?如果它没有被声明为瞬态,那么无论如何都会写出来,对吗?

6 个答案:

答案 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)

readObjectwriteObjectObject(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方法的使用。