Why are readObject and writeObject private, and why would I write transient variables explicitly?
I am reading the chapter on Serialization in Effective Java.
Who calls the readObject() and writeObject()? Why are these methods declared private ?
-
The below is a piece of code from the book
// 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); } } }
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.