开发者

Why are readObject and writeObject private, and why would I write transient variables explicitly?

开发者 https://www.devze.com 2023-04-06 06:58 出处:网络
I am reading the chapter on Serialization in Effective Java. Who calls the readObject() and writeObject()? Why are these methods declared private ?

I am reading the chapter on Serialization in Effective Java.

  1. Who calls the readObject() and writeObject()? Why are these methods declared private ?

  2. The below is a piece of code from the book

    // StringList with a reasonable custom serialize开发者_如何学Cd 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);
            }
        }
    }
    

    Is there any specific reason the variable size is declared as transient and then in the writeObject method it is explicitly written? If it were not declared as transient, it would have been written anyway, right?


(1) The methods are not declared in any class or interface. A class, that implements the Serializable interface and requires special special handling during the serialization and deserialization process must implement those methods and the serializer/deserializer will try to reflect those methods.

This is one of the rather strange corners in Java where the API is actually defined in the javaDoc... But if the methods had been defined in an interface, then they had to be public (we can't implement an interface method an lock it by adding a private modifier).

Why private - the javaDoc does not give a hint. Maybe they are specified as private because no other class but the implementor is intended to use them. They are private by definition.

(2) The example simply shows how the special handling works. In this example, size is transient and will not be serialized. But now we introduce the special handler and this handler adds the value of size to the stream. The difference to the normal approach with non-transient fields could be the order of elements in the resulting stream (if it matters...).

The example could make sense, if the transient field was defined in a super class and a subclass wanted to serialize the value.


Apart from should not be used by wrong parties, here is another reason for the privacy of these methods:

We don't want these methods to be overridden by subclasses. Instead, each class can have its own writeObject method, and the serialization engine will call all of them one after the other. This is only possible with private methods (these are not overridden). (The same is valid for readObject.)

(Note that this only applies to superclasses which themselves implement Serializable.)

This way, subclasses and superclasses can evolve independently, and still stay compatible to stored objects from older versions.


About readObject()/writeObject() being private, here's the deal: if your class Bar extends some class Foo; Foo also implements readObject()/writeObject() and Bar also implements readObject()/writeObject().

Now, when a Bar object is serialized or deserialized, JVM needs to call readObject()/writeObject() for both Foo and Bar automatically (i.e. without you needing to class these super class methods explicitly).However, if these methods are anything but private, it becomes method overriding, and JVM can no longer call the super class methods on the sub class object.

Hence they must be private!


The readObject and writeObject are called by the Object(Input/Output)Stream class(es).

These methods are (and must be) declared private (when implementing your own), proving/indicating that neither method is inherited and overridden or overloaded by the implementation. The trick here is that the JVM automatically checks to see if either method is declared during the corresponding method call. Note that the JVM can call private methods of your class whenever it wants but no other objects can. Thus, the integrity of the class is maintained and the serialization protocol can continue to work as normal.

And regarding the transient int, it's just taking control over serialization of the entire object serialization as such. However, note that technically it is not even necessary to invoke defaultWriteObject(), if all fields are transient. But i think it is still recommended to invoke it for flexiblility purposes, so that later you can introduce non-transient members in your class, preserving compatibility.


Regarding the transient variable, best way to understand as if why do we declare transient variable and later on serialize them in writeobject method is to check/analyze/debug readobject/writeobject methods of LinkedList/HashMap/etc classes.

This is generally done when you want to serialize/de-serialize the class variables in a pre-defined order and not rely on the default behavior/order.


Assume you have a Class A which has reference to a Socket. If you want to serialize objects of class A you can't directly because Socket is not Serializable . In this case you write code as below.

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);
}
}

Ignore any networking related issues since purpose is to show use of writeObject/ readObject methods.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号