Java serialization: readObject() vs. readResolve()

JavaSerializationSingleton

Java Problem Overview


The book Effective Java and other sources provide a pretty good explanation on how and when to use the readObject() method when working with serializable Java classes. The readResolve() method, on the other hand, remains a bit of a mystery. Basically all documents I found either mention only one of the two or mention both only individually.

Questions that remain unanswered are:

  • What is the difference between the two methods?
  • When should which method be implemented?
  • How should readResolve() be used, especially in terms of returning what?

I hope you can shed some light on this matter.

Java Solutions


Solution 1 - Java

readResolve is used for replacing the object read from the stream. The only use I've ever seen for this is enforcing singletons; when an object is read, replace it with the singleton instance. This ensures that nobody can create another instance by serializing and deserializing the singleton.

Solution 2 - Java

Item 90, Effective Java, 3rd Ed covers readResolve and writeReplace for serial proxies - their main use. The examples do not write out readObject and writeObject methods because they are using default serialisation to read and write fields.

readResolve is called after readObject has returned (conversely writeReplace is called before writeObject and probably on a different object). The object the method returns replaces this object returned to the user of ObjectInputStream.readObject and any further back references to the object in the stream. Both readResolve and writeReplace may return objects of the same or different types. Returning the same type is useful in some cases where fields must be final and either backward compatibility is required or values must copied and/or validated.

Use of readResolve does not enforce the singleton property.

Solution 3 - Java

readResolve can be used to change the data that is serialized through readObject method. For e.g. xstream API uses this feature to initialize some attributes that were not in the XML to be deserialized.

http://x-stream.github.io/faq.html#Serialization

Solution 4 - Java

readObject() is an existing method in ObjectInputStream class. At the time of deserialization readObject() method internally checks whether the object that is being deserialized has readResolve() method implemented. If readResolve() method exists then it will be invoked

A sample readResolve() implementation would look like this

protected Object readResolve() {
  return INSTANCE:
}

So, the intent of writing readResolve() method is to ensure that the same object that lives in JVM is returned instead of creating new object during deserialization.

Solution 5 - Java

readResolve is for when you may need to return an existing object, e.g. because you're checking for duplicate inputs that should be merged, or (e.g. in eventually-consistent distributed systems) because it's an update that may arrive before you're aware of any older versions.

Solution 6 - Java

readResolve() will ensure the singleton contract while serialization.
Please refer

Solution 7 - Java

As already answered, readResolve is an private method used in ObjectInputStream while deserializing an object. This is called just before actual instance is returned. In case of Singleton, here we can force return already existing singleton instance reference instead of deserialized instance reference. Similary we have writeReplace for ObjectOutputStream.

Example for readResolve:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class SingletonWithSerializable implements Serializable {
private static final long serialVersionUID = 1L;

public static final SingletonWithSerializable INSTANCE = new SingletonWithSerializable();

private SingletonWithSerializable() {
	if (INSTANCE != null)
		throw new RuntimeException("Singleton instance already exists!");
}

private Object readResolve() {
	return INSTANCE;
}

public void leaveTheBuilding() {
	System.out.println("SingletonWithPublicFinalField.leaveTheBuilding() called...");
}

public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
	SingletonWithSerializable instance = SingletonWithSerializable.INSTANCE;

	System.out.println("Before serialization: " + instance);

	try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("file1.ser"))) {
		out.writeObject(instance);
	}

	try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("file1.ser"))) {
		SingletonWithSerializable readObject = (SingletonWithSerializable) in.readObject();
		System.out.println("After deserialization: " + readObject);
	}

}

}

Output:

Before serialization: com.ej.item3.SingletonWithSerializable@7852e922
After deserialization: com.ej.item3.SingletonWithSerializable@7852e922

Solution 8 - Java

When serialization is used to convert an object so that it can be saved in file, we can trigger a method, readResolve(). The method is private and is kept in the same class whose object is being retrieved while deserialization. It ensures that after the deserialization, what object is returned is the same as was serialised. That is, instanceSer.hashCode() == instanceDeSer.hashCode()

readResolve() method is not a static method. After in.readObject() is called while deserialisation it just makes sure that the returned object is the same as the one which was serialized as below while out.writeObject(instanceSer)

..
    ObjectOutput out = new ObjectOutputStream(new FileOutputStream("file1.ser"));
    out.writeObject(instanceSer);
    out.close();

In this way, it also helps in singleton design pattern implementation, because every time same instance is returned.

public static ABCSingleton getInstance(){
    return ABCSingleton.instance; //instance is static 
}

Solution 9 - Java

I know this question is really old and has an accepted answer, but as it pops up very high in google search I thought I'd weigh in because no provided answer covers the three cases I consider important - in my mind the primary use for these methods. Of course, all assume that there is actually a need for custom serialization format.

Take, for example collection classes. Default serialization of a linked list or a BST would result in a huge loss of space with very little performance gain comparing to just serializing the elements in order. This is even more true if a collection is a projection or a view - keeps a reference to a larger structure than it exposes by its public API.

  1. If the serialized object has immutable fields which need custom serialization, original solution of writeObject/readObject is insufficient, as the deserialized object is created before reading the part of the stream written in writeObject. Take this minimal implementation of a linked list:

    public class List<E> extends Serializable {
        public final E head;
        public final List<E> tail;
    
        public List(E head, List<E> tail) {
            if (head==null)
                throw new IllegalArgumentException("null as a list element");
            this.head = head;
            this.tail = tail;
        }
    
        //methods follow...
    }
    

This structure can be serialized by recursively writing the head field of every link, followed by a null value. Deserializing such a format becomes however impossible: readObject can't change the values of member fields (now fixed to null). Here come the writeReplace/readResolve pair:

private Object writeReplace() {
    return new Serializable() {
        private transient List<E> contents = List.this;
        
        private void writeObject(ObjectOutputStream oos) {
            List<E> list = contents;
            while (list!=null) {
                oos.writeObject(list.head);
                list = list.tail;
            }
            oos.writeObject(null);
        }

        private void readObject(ObjectInputStream ois) {
            List<E> tail = null;
            E head = ois.readObject();
            if (head!=null) {
                readObject(ois); //read the tail and assign it to this.contents
                this.contents = new List<>(head, this.contents)
            }                     
        }


        private Object readResolve() {
            return this.contents;
        }
    }
}

I am sorry if the above example doesn't compile (or work), but hopefully it is sufficient to illustrate my point. If you think this is a very far fetched example please remember that many functional languages run on the JVM and this approach becomes essential in their case.

  1. We may want to actually deserialize an object of a different class than we wrote to the ObjectOutputStream. This would be the case with views such as a java.util.List list implementation which exposes a slice from a longer ArrayList. Obviously, serializing the whole backing list is a bad idea and we should only write the elements from the viewed slice. Why stop at it however and have a useless level of indirection after deserialization? We could simply read the elements from the stream into an ArrayList and return it directly instead of wrapping it in our view class.

  2. Alternatively, having a similar delegate class dedicated to serialization may be a design choice. A good example would be reusing our serialization code. For example, if we have a builder class (similar to the StringBuilder for String), we can write a serialization delegate which serializes any collection by writing an empty builder to the stream, followed by collection size and elements returned by the colection's iterator. Deserialization would involve reading the builder, appending all subsequently read elements, and returning the result of final build() from the delegates readResolve. In that case we would need to implement the serialization only in the root class of the collection hierarchy, and no additional code would be needed from current or future implementations, provided they implement abstract iterator() and builder() method (the latter for recreating the collection of the same type - which would be a very useful feature in itself). Another example would be having a class hierarchy which code we don't fully control - our base class(es) from a third party library could have any number of private fields we know nothing about and which may change from one version to another, breaking our serialized objects. In that case it would be safer to write the data and rebuild the object manually on deserialization.

Solution 10 - Java

The readResolve Method

For Serializable and Externalizable classes, the readResolve method allows a class to replace/resolve the object read from the stream before it is returned to the caller. By implementing the readResolve method, a class can directly control the types and instances of its own instances being deserialized. The method is defined as follows:

ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;

The readResolve method is called when ObjectInputStream has read an object from the stream and is preparing to return it to the caller. ObjectInputStream checks whether the class of the object defines the readResolve method. If the method is defined, the readResolve method is called to allow the object in the stream to designate the object to be returned. The object returned should be of a type that is compatible with all uses. If it is not compatible, a ClassCastException will be thrown when the type mismatch is discovered.

For example, a Symbol class could be created for which only a single instance of each symbol binding existed within a virtual machine. The readResolve method would be implemented to determine if that symbol was already defined and substitute the preexisting equivalent Symbol object to maintain the identity constraint. In this way the uniqueness of Symbol objects can be maintained across serialization.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionForageView Question on Stackoverflow
Solution 1 - JavaMichael MyersView Answer on Stackoverflow
Solution 2 - JavaTom Hawtin - tacklineView Answer on Stackoverflow
Solution 3 - JavaendlessView Answer on Stackoverflow
Solution 4 - Javaarjun kumarView Answer on Stackoverflow
Solution 5 - JavaPr0metheanView Answer on Stackoverflow
Solution 6 - JavaKanagavelu SugumarView Answer on Stackoverflow
Solution 7 - JavaOmkar ShetkarView Answer on Stackoverflow
Solution 8 - Javahi.nitishView Answer on Stackoverflow
Solution 9 - JavaTurinView Answer on Stackoverflow
Solution 10 - JavaAnkush soniView Answer on Stackoverflow