Using Serialization in Java is pretty simple. If you have some object you would like to serialize, you just make it implement the Serializable interface. Then, you can use the ObjectOutputStream to store that object to a file or send it to some other machine. All non-transient and non-static fields will be serialized and even care will be taken to reproduce exactly the same object graph during deserialization (for example when you have many references pointing to the same object). Sometimes however, you would like to implement your own way of serializing/deserializing an object. This way you could gain more control in cases that require special handling. Let’s see a simple example below.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: |
class SessionDTO implements Serializable {
private static final long serialVersionUID = 1L;
private int data; // Stores session data
// Session activation time (creation, deserialization)
private long activationTime;
public SessionDTO(int data) {
this.data = data;
this.activationTime = System.currentTimeMillis();
}
public int getData() {
return data;
}
public long getActivationTime() {
return activationTime;
}
}
|
And here some sample main method that serializes the above class to a file and deserializes it back.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: |
public class SerializeTester implements Serializable {
public static void main(String... strings) throws Exception {
File file = new File("out.ser");
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(file));
SessionDTO dto = new SessionDTO(1);
oos.writeObject(dto);
oos.close();
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(file));
SessionDTO dto = (SessionDTO) ois.readObject();
System.out.println("data : " + dto.getData()
+ " activation time : " + dto.getActivationTime());
ois.close();
}
}
|
The class SessionDTO represents some session that we want to transfer between two servers. It has some informations stored in the field data that need to be serialized. But there is also a field activationTime, which should store the time the session appeared for the first time on the specific server. It is not a kind of information we would like to be transferred. The field should be set just after/during the deserialization. Furthermore, there is no need to include it in the stream being sent between the servers, since it would be only unnecessarily taking space.
A solution in such a situation is to use the writeObject and readObject methods. There is a chance that some of you never heard of them since they are being omitted in many books about Java, and they are also not part of the most popular Java examinations. Let’s rewrite our SessionDTO using those methods:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: |
class SessionDTO implements Serializable {
private static final long serialVersionUID = 1L;
private transient int data; // Stores session data
//Session activation time (creation, deserialization)
private transient long activationTime;
public SessionDTO(int data) {
this.data = data;
this.activationTime = System.currentTimeMillis();
}
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
oos.writeInt(data);
System.out.println("session serialized");
}
private void readObject(ObjectInputStream ois) throws IOException,
ClassNotFoundException {
ois.defaultReadObject();
data = ois.readInt();
activationTime = System.currentTimeMillis();
System.out.println("session deserialized");
}
public int getData() {
return data;
}
public long getActivationTime() {
return activationTime;
}
}
|
Method writeObject handles serialization of the object. If it is declared, it will be called from the ObjectOutputStream instead of the default serialization process. If you are seeing it for the first time, you may be surprised by the fact that both methods are private and though they will be called from outside of the class. Also they are not part of the java.lang.Object class, nor are they declared in the Serializable interface. So how can ObjectOutputStream use them? Well, ObjectOutputStream uses reflection to find out if those methods are declared. It uses getPrivateMethod so those methods
On the beginning of both methods you can see calls to defaultWriteObject() and defaultReadObject() methods. What they do is the default serialization process like writing/reading all the non-transient and non-static fields (but they do not include the check for serialVersionUID, which is done every time). Generally, all the fields that we want to handle by ourselves should be declared transient. This way defaultWriteObject/defaultReadObject care for all the other fields and we specify the serialization behavior for those critical ones. Using those two default methods is not compulsory but gives more flexibility when dealing with more complicated cases.
You can read a lot about serialization for example on this article or on this guide.
The case described here was very simple. In one of my next posts I will show the usage of readResolve/writeReplace and serialization working with class inheritance.
2 Comments until now
Nice article.
[...] This short introduction shows just a little bit of what actually can be done with XML serialization. You can read much more about it for example here. We have also an entry on this blog about customizing the serialization process in general (using writeObject and readObject methods), which you can read here. [...]
Add your Comment!