When you serialize an object in Java it is converted to byte stream and object is reconstituted using that byte stream during the process of deserialization. Sometimes this extraneous behavior of creating object using the byte stream is not what you want and you still want constructor (or any other method if required) to be called when your object is created during the process of deserialization.
In a scenario like this you can use serialization proxy pattern where you serialize a proxy object rather than the real object, at the time of deserialization using that proxy object you can create the real object by calling the constructor (or any other required method).
Serialization proxy pattern
Serialization proxy pattern is a way to design your class where proxy pattern defines its serialization mechanism.
Before going into any more details about serialization proxy pattern in Java let’s know about two methods.
- writeReplace()
- readResolve()
writeReplace() method in Java
Serializable classes that use an alternative object (proxy object) when writing an object to the stream should implement writeReplace() method with the exact signature:
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
This writeReplace() method is invoked by serialization if the method exists and this method is defined within the original class whose object is serialized. Thus, the method can have private, protected and package-private access modifier.
readResolve() method in Java
Classes that need to provide a replacement object when the serialized object is read from the stream should implement readResolve() method with the exact signature.
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
This readResolve() method follows the same invocation rules and accessibility rules as writeReplace.
How Serialization Proxy pattern works in Java
Rather than serializing the original class you provide functionality using the writeReplace() method to serialize the proxy class instead. Here note that writeReplace() method is implemented in the original class.
At the time of deserialization proxy object is deserialized and then the readResolve() method is called. That’s where you will have to provide the functionality to create the original class object regular way. Here note that readResolve() method is implemented in the proxy class.
Proxy class
Generally serialization proxy pattern is implemented by creating a proxy class as a nested static class with in the original class. Since it needs access to the fields of the outer class so that it is better to create proxy class as a nested class.
Serialization proxy pattern example
Time is right to see an example of the serialization proxy pattern in Java. Here we have a Person class which has a constructor with args. When a Person class object is created it is initialized using this constructor and that’s what you want to do when you deseliarize a serialized Person class object.
For doing that you will use Serialization proxy pattern and create a proxy class (called PersonProxy here) as a nested static class. You will also implement writeReplace() and readResolve() methods.
Person class
import java.io.Serializable; public class Person implements Serializable{ private static final long serialVersionUID = 9140203997753929147L; private String name; private int id; private int age; // Constructor Person(String name, int id, int age){ System.out.println("In Constructor with args"); this.name = name; this.id = id; this.age = age; } // no-arg Constructor Person(){ System.out.println("no-arg constructor"); } public String getName() { return name; } public int getAge() { return age; } public int getId() { return id; } /** * writeReplace method for the proxy pattern * @return */ private Object writeReplace() { System.out.println("In writeReplace() method"); return new PersonProxy(this); } // Nested static class - Proxy private static class PersonProxy implements Serializable { private static final long serialVersionUID = -5965328891170223339L; private String name; private int id; private int age; PersonProxy(Person p) { this.name = p.name; this.id = p.id; this.age = p.age; } // readResolve method for Person.PersonProxy private Object readResolve() { System.out.println("In readResolve() method"); return new Person(name, id, age); // Uses public constructor } } }
Util class
A util class with methods to serialize and deserialize.
import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class Util { /** * Method used for serialization * @param obj * @param fileName */ public static void serialzeObject(Object obj, String fileName){ try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File(fileName)))){ oos.writeObject(obj); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * Method used for deserializing * @param fileName * @return * @throws ClassNotFoundException */ public static Object deSerialzeObject(String fileName) throws ClassNotFoundException{ Object obj = null; try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File(fileName)))){ obj = ois.readObject(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return obj; } }
SerializationDemo class
Using this class a Person class object will be serialized and later deserialized.
public class SerializationDemo { public static void main(String[] args) { // Creating and initializaing a Person object Person person = new Person("User1", 1, 22); // file name final String fileName = "F://person.ser"; System.out.println("About to serialize ...."); // serializing Util.serialzeObject(person, fileName); try { System.out.println("About to deserialize ...."); // deserializing person = (Person)Util.deSerialzeObject(fileName); System.out.println("id " + person.getId() + " Name "+ person.getName() + " Age " + person.getAge()); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
Output
In Constructor with args About to serialize .... In writeReplace() method About to deserialize .... In readResolve() method In Constructor with args id 1 Name User1 Age 22
Here you can see that writeReplace() method is called when object is serialized. At the time of deserializing the object readResolve() method is called where object is created and initialized using the constructor of the class not just recreated using the byte stream.
Creating instance by getting data from DB
Another serialization proxy pattern usage example, which you will see in many frameworks too is when you want your instance to be created using a DB call. In that case what you need to serialize is some identifier only and during deserialization using that identifier you will get the data to construct the object from DB.
Here you have a Person class with fields as id, name etc. In writeReplace() method where you serialize the proxy you provide the id also.
Person class
public class Person implements Serializable { private int id; private String name; … private Object writeReplace() { return new PersonProxy(id); } }
Proxy Class
In the readResolve() method of the proxy class you create the Person class instance using the id which you saved earlier.
public class PersonProxy implements Serializable { private int id; public PersonProxy(int id) { this.id = id; } public Object readResolve() { // DB call to get the person record by id return PersonDAO.findById(id); } }
That's all for this topic Serialization Proxy pattern in Java. If you have any doubt or any suggestions to make please drop a comment. Thanks!
>>>Return to Java Advanced Tutorial Page
Related Topics
You may also like-