Serialization in Java
An object's state can be transformed into a byte stream through a process known as serialization. Deserialization is the reverse procedure, in which the actual Java object is recreated in memory using the byte stream. The generated byte stream is cross-platform. Therefore, an object that has been serialized on one platform can be deserialized on another platform.
Introduction to Serialization in Java
Ever wondered in the Java world, how data is sent over the network or how you’re able to save files on a disk. Well, all this is possible thanks to something called Serialization and Deserialization.
Serialization is the process of converting the state of an object into a byte stream. A byte-stream is a Java I/O (Input/Output) stream which is essentially flows of data that a programmer can either read from or write to. Byte Streams read or write one byte of data at a time.
Great! Now we have converted the object into a byte-stream which can be saved onto a disk, communicated over the network, etc.
However, to use them again, we need to convert these byte-streams back to their respective Objects. This reverse process of converting object into byte-stream is called Deserialization.
The image is a simple illustration of the Serialization-Deserialization process in Java.
The byte stream which is created during Serialization is not dependent on platform so object Serialized on one platform can be Deserialized on other platform also.
Why Do We Need Serialization in Java?
As mentioned in the starting, Serialization mechanism is usually used when there is a need to send your data (objects) over the network or to store in files. Now the hardware components like network infrastructure, hard disk etc understands only bytes and bits, not the java objects.
So Serialization is used in this case which translates Java object's state to byte-stream to send it over the network or save it in file.
How Do We Serialize An Object?
Serialization in java can be implemented using java.io.Serializable interface. writeObject() method of ObjectOutputStream class is used for serializing an Object. To make a Java object serializable we implement the java.io.Serializable interface.
Let’s look at one example to show how serialization in Java works programmatically. Here we are creating a Student class and are serializing the object of the Student class.
- The code snippet shows serialization of an Object of type Student. A text file called storeObject is created with the help of the FileOutputStream class.
- FileOutputStream is an output stream which is used for writing data into a file. Next the ObjectOutputStream class is used to write the object to an OutputStream.
- writeObject(Object ob) is used to serialize objects into byte-streams.
- So the object is converted to a byte-stream. Hence, serialization is complete.
Next let’s look at the code for deserializing the same object. For deserializing the object we will be using the readObject() method of ObjectInputStream class.
- Just like the previous code snippet the object is first serialized.
- Here, the storeObject.txt file is accessed with the help of the FileInputStream which is used to read data from a file.
- Next, the object is retrieved with the help of the ObjectInputStream which converts the objects from the byte-stream which was written using ObjectOutputStream.
- readObject() method is used to deserialize objects.
Hence, the process from object to byte-stream is complete.
While Serialization the writeObject() method is used. Whereas, in Deserialization the readObject() method is used. Also, for an object to be made serializable it is absolutely mandatory for the object’s class to implement the Serializable interface.
An interface is like a class but with some differences. An interface can have methods and variables just like the class but the methods declared in interface are by default abstract, that is only method signature, no body. This is an important difference.
Basically, interfaces are used to show only the important things to the user and hide internal working details away from the user.
The Serializable interface tells the JVM (Java Virtual Machine) that the objects of this class are ready for serialization and/or deserialization. The code that we write is called source code. We know that machines do not understand the human language. To bridge the gap we need to translate the source code.
This is where the JVM comes in. It converts source code to something called bytecode which is then translated to the machine language.
Another concept that is important is Serial Version UID.
What is Serial Version UID ?
The serialVersionUID is a constant. So while the whole object to byte-stream to object takes place, we need to be sure that the conversion from byte-stream to object is correct.
- We use the serialVersionUID attribute to remember versions of a Serializable class to verify that a class and the serialized object are compatible.
- Please note that this serialVersionUID is optional. If the programmer does not define this constant then Java does it for us.
The serialization at runtime associates with each serializable class a serialVersionUID, which is used during deserialization to verify that the object that was converted to byte-stream is the one that is being retrieved.
What if we do not want to save, i.e. serialize, the state of some data member of the class that gets serialized?
In that case, we use a reserved keyword with the data member(s) that we do not want to serialize like below:
Take a look at the example below:
The last print statement will return null as stu_Addr is a transient variable and as such it will not get serialized and will lose its value.
Space is allocated to transient variables as these do not get converted to byte streams. This results in inefficient memory utilization. When a class gets serialized we are unable to use it till we convert it back from byte stream.
Serialization in Java With Inheritance
When a parent class implements the Serializable interface, the child classes do not have to do so. This is a case of Serialization with Inheritance. Inheritance is when a child class extends a parent class and inherits it’s properties. Hence, the names - parent and child.
Since Student extends Child which extends the Serializable interface there is no need for the Student class itself to extend this interface.
Serialization in Java With Aggregation
If a class Student has an object of type Child and if class Student were to implement the Serializable interface and not B then Java would throw a NotSerializableException. Let’s see in code:
The above code snippet would throw a NotSerializableException when an object of class Student is serialized. Class Student is said to have a relationship with class Child. This is called Aggregation (HAS-A Relationship).
Serialization in Java With Static data member
Static data members are volatile to change during the serialization to deserialization process. Static means the data member is the only copy of the class and any change made to it will be felt throughout the program.
In other words, it belongs to the whole class. Consider below code where x is a static variable:
- In the above code, class has a static member of type int, with the value 50, when an object of this class was serialized. After this object was serialized, the value of the static member is changed to 48 by the program.
- When this object is deserialized, the value of static member is not restored to its original value of 50 which was the value when the object was serialized, because the static member belong to its class and not the object.
- Hence, the process of serialization and deserialization only works on the object of a class and it doesn't affect a static member of a class, and therefore, when object was deserialized, the value of static member is found to be 48 and not 50.
- Serialization is the mechanism of converting the state of an object into a byte - stream and Deserialization is the mechanism of converting a byte-stream again into an object.
- We perform serialization in Java using the writeObject(Objet ob) method and deserialization is performed using the readObject() method.
- We use the serialVersionUID attribute to remember versions of a Serializable class to verify that a loaded class and the serialized object are compatible.
- If the parent class implement java.io.Serializable, its child objects need not implement java.io.Serializable to be serializable.
- If a class contains an object of another non-serializable class as its data member, then it cannot be serialized.
- Serialization does not affect the static members of a class.