In my last post, I talked about serialization in general. This one is much more focused and presents a single detail: the Serialization Proxy Pattern. It is a good and often the best way to deal with many of the issues with serialization. If there was only one thing a developer would want to know about the topic, I'd tell them this.
As far as I know, the pattern was first defined in Joshua Bloch's excellent book Effective Java (1st edition: item 57; 2nd edition: item 78). This post mostly restates what is said there and focuses on presenting a detailed definition of the pattern before giving two short examples and finally covering the pros and cons.
The code samples used throughout this post come from a demo project I created on GitHub. Check it out for more details!
▚Serialization Proxy Pattern
This pattern is applied to a single class and defines its mechanism of serialization. For easier readability, the following text will refer to that class or its instances as the original one or ones, respectively.
▚The Serialization Proxy
As the name suggests the pattern's key is the serialization proxy. It is written to the byte stream instead of the original instance. After it is deserialized it will create an instance of the original class which takes its place in the object graph.
The goal is to design the proxy such that it is the best possible logical representation of the original class.
▚Implementation
The SerializationProxy
is a static nested class of the original class.
All its fields are final and its only constructor has an original instance as its sole argument.
It extracts the logical representation of that instance's state and assigns it to its own fields.
As the original instance is considered "safe", there is no need for consistency checks or defensive copying.
The original as well as the proxy class implement Serializable. But since the former is never actually written to the stream, only the latter needs a stream unique identifier (often called the serial version UID).
▚Serializing
When an original instance is to be serialized, the serialization system can be informed to instead write the proxy to the byte stream. To do this, the original class must implement the following method:
private Object writeReplace() {
return new SerializationProxy(this);
}
▚Deserializing
On deserialization this translation from original to proxy instance has to be inverted.
This is implemented in the following method in the SerializationProxy
, which is called after a proxy instance was successfully deserialized:
private Object readResolve() {
// create an instance of the original class
// in the state defined by the proxy's fields
}
Creating an instance of the original class will be done via its regular API (e.g. a constructor).