Serialization in Java — Java Serialization
Have you ever seen some code examples that implements Serializable interface?
Did you question yourself why do we need serialization for objects? If yes then you are in the right place :)
Serialization in Java allows us to convert an Object to stream that we can send over the network or save it as file or store in DB for later usage. Deserialization is the process of converting Object stream to actual Java Object to be used in our program.
Serialization in Java seems very easy to use at first but it comes with some trivial security and integrity issues that we will look in the later part of this article. We will cover the following topics in this article
- 1. Serializable in Java
- 2. Class Refactoring with Serialization and serialVersionUID
- 3. Java Serialization Methods
Serializable in Java
If you want a class object to be serializable, all you need to do it implement the java.io.Serializable
interface. Serializable in java is a marker interface and has no fields or methods to implement. It’s like an Opt-In process through which we make our classes serializable. Serialization in java is implemented by ObjectInputStream
and ObjectOutputStream
, so all we need is a wrapper over them to either save it to file or send it over the network.
Notice that it’s a simple java bean with some properties and getter-setter methods. If you want an object property to be not serialized to stream, you can use transient keyword like I have done with grade variable. Now suppose we want to write our objects to file and then deserialize it from the same file. So we need utility methods that will use ObjectInputStream
and ObjectOutputStream
for serialization purposes.
Notice that the method arguments work with Object that is the base class of any java object. It’s written in this way to be generic in nature. The Object class provides some common behaviors to all the objects.
Now let’s test our code:
Output of the above code would be:
Since grade is a transient variable, it’s value was not saved to file and hence not retrieved in the new object. Similarly static variable values are also not serialized since they belongs to class and not object.
Class Refactoring with Serialization and serialVersionUID
Serialization in java permits some changes in the java class if they can be ignored. Some of the changes in class that will not affect the deserialization process are:
- Adding new variables to the class
- Changing the variables from transient to non-transient, for serialization it’s like having a new field.
- Changing the variable from static to non-static, for serialization it’s like having a new field.
But for all these changes to work, the java class should have serialVersionUID defined for the class. Let’s write a test class just for deserialization of the already serialized file from previous test class.
Now add new “course” variable and it’s getter-setter methods from Student class and run it. You will get below exception:
The reason is clear that serialVersionUID of the previous class and new class are different. Actually if the class doesn’t define serialVersionUID, it’s getting calculated automatically and assigned to the class. Java uses class variables, methods, class name, package etc to generate this unique long number. If you are working with any IDE, you will automatically get a warning that “The serializable class Student does not declare a static final serialVersionUID field of type long”.
Note that it’s not required that the serial version is generated from this program itself, we can assign this value as we want. It just need to be there to let deserialization process know that the new class is the new version of the same class and should be deserialized of possible.
Java Serialization Methods
We have seen that serialization in java is automatic and all we need is implementing Serializable interface. The implementation is present in the ObjectInputStream and ObjectOutputStream classes. But what if we want to change the way we are saving data, for example we have some sensitive information in the object and before saving/retrieving we want to encrypt/decrypt it. That’s why there are four methods that we can provide in the class to change the serialization behavior. If these methods are present in the class, they are used for serialization purposes.
- readObject(ObjectInputStream ois): If this method is present in the class, ObjectInputStream readObject() method will use this method for reading the object from stream.
- writeObject(ObjectOutputStream oos): If this method is present in the class, ObjectOutputStream writeObject() method will use this method for writing the object to stream. One of the common usage is to obscure the object variables to maintain data integrity.
- Object writeReplace(): If this method is present, then after serialization process this method is called and the object returned is serialized to the stream.
- Object readResolve(): If this method is present, then after deserialization process, this method is called to return the final object to the caller program. One of the usage of this method is to implement Singleton pattern with Serialized classes. Read more at Serialization and Singleton.
Usually while implementing above methods, it’s kept as private so that subclasses can’t override them. They are meant for serialization purpose only and keeping them private avoids any security issue.
I think you get it :)
All sources for writing this article is based on digitalocean and javapoint