Search code examples
javaserializationeffective-java

Java: When to add readObjectNoData() during serialization?


I am reading the serialization chapter in Effective Java. I am trying to understand the paragraph below, which is found in the book.

If you implement a class with instance fields that is serializable and extendable,there is a caution you should be aware of. If the class has invariants that would be violated if its instance fields were initialized to their default values (zero for integral types, false for boolean, and null for object reference types), you must add this readObjectNoData method to the class:

// readObjectNoData for stateful extendable serializable classes
private void readObjectNoData() throws InvalidObjectException {
    throw new InvalidObjectException("Stream data required");
}

I am not sure what that statement means.

To test this, I created a class called Person (both serializable and extendable)

class Person implements Serializable {

    private String name;
    private int age;

    Person() {
        this("default", 1);
    }
    
    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

– and a class: Employee, which extends it.

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

Are there any invariants in the Person class I created? When will they be violated? I copy-pasted the code for the readObjectData() method in the Employee class, but it never got called. When will the readObject() method be called? Am I missing something?


Solution

  • The readObjectNoData section in Java Object Serialization Specification seems interesting (see below).

    Your edits to the question give a perfect example. If Employee was serialized when it did not extend Person and later deserialized when it did then the Person part would be initialized to empty string and 0 age. Using this method, you can initialize them to "name" and 1 respectively.

    For serializable objects, the readObjectNoData method allows a class to control the initialization of its own fields in the event that a subclass instance is deserialized and the serialization stream does not list the class in question as a superclass of the deserialized object. This may occur in cases where the receiving party uses a different version of the deserialized instance's class than the sending party, and the receiver's version extends classes that are not extended by the sender's version. This may also occur if the serialization stream has been tampered; hence, readObjectNoData is useful for initializing deserialized objects properly despite a "hostile" or incomplete source stream.

    private void readObjectNoData() throws ObjectStreamException;

    Each serializable class may define its own readObjectNoData method. If a serializable class does not define a readObjectNoData method, then in the circumstances listed above the fields of the class will be initialized to their default values (as listed in section 4.5.5 of The JavaTM Language Specification, Second Edition); this behavior is consistent with that of ObjectInputStream prior to version 1.4 of the JavaTM 2 SDK, Standard Edition, when support for readObjectNoData methods was introduced. If a serializable class does define a readObjectNoData method and the aforementioned conditions arise, then readObjectNoData will be invoked at the point during deserialization when a class-defined readObject method would otherwise be called had the class in question been listed by the stream as a superclass of the instance being deserialized.