Serialization & Deserialization

About

Serialization and Deserialization are processes in Java (and in other programming languages) that involve converting an object into a stream of bytes to store its state persistently or to transmit it over a network, and then reconstructing the object from that stream.

Whenever an object is Serialized, the object is stamped with a version ID number for the object class. This ID is called the SerialVersionUID. This is used during deserialization to verify that the sender and receiver that are compatible with the Serialization.

Serialization

Serialization is the process of converting an object into a byte stream so that it can be stored in a file, sent over a network, or persisted in a database.

Purpose

  • Persistence: Save the state of an object for later retrieval.

  • Communication: Transmit objects between applications or across a network.

Mechanism

  • In Java, the Serializable interface marks a class as serializable. It is a marker interface without any methods.

  • Objects of a serializable class can be converted into a stream of bytes using ObjectOutputStream.

  • Variables that are marked as transient will not be a part of the serialization. So we can skip the serialization for the variables in the file by using a transient keyword.

Example

Deserialization

Deserialization is the process of reconstructing an object from a serialized byte stream.

Purpose

  • Restore the state of an object previously serialized.

  • Receive and reconstruct objects transmitted over a network or read from storage.

Mechanism

  • In Java, use ObjectInputStream to read the serialized byte stream and reconstruct the object.

  • The class of the deserialized object must have the same serialVersionUID as when it was serialized to ensure compatibility.

Example

About serialVersionUID

serialVersionUID is a unique identifier for a Serializable class in Java. It ensures version compatibility between serialized and deserialized objects.

Why is serialVersionUID Needed?

During deserialization, Java checks whether the class used to serialize an object matches the class definition available at runtime. This is done by comparing the serialVersionUID of the serialized object and the current class. If they don’t match, InvalidClassException is thrown.

Default Behavior

If serialVersionUID is not explicitly declared, Java generates it based on various class properties such as fields, methods, constructors, and other structural elements. This means even a minor change in the class (e.g., renaming a method) can alter the generated serialVersionUID, causing incompatibility.

Declaring serialVersionUID

To avoid unexpected deserialization failures, we can manually define serialVersionUID

Generating serialVersionUID Automatically

How Does It Work?

  1. When an object is serialized, its serialVersionUID is stored in the serialized data.

  2. During deserialization, Java checks if the serialVersionUID of the saved object matches the current class.

  3. If they match, deserialization proceeds. Otherwise, an InvalidClassException occurs.

Best Practices

  1. Always define serialVersionUID explicitly to prevent accidental changes from breaking deserialization.

  2. Use a meaningful value (e.g., increment it when making incompatible changes).

  3. Keep it the same if changes are backward-compatible (e.g., adding non-final, non-static fields).

  4. Use serialVersionUID for versioning to control object evolution across different releases.

Handling Class Changes with Default serialVersionUID

Change Type
Impact on Deserialization

Adding new non-static, non-final fields

Compatible

Changing method implementation

Compatible

Removing a field

Compatible (field gets default value)

Changing field type

Incompatible (causes InvalidClassException)

Renaming a field

Incompatible (treated as removal and addition)

Scenario 1: serialVersionUID Matches (Successful Deserialization)

Step 1: Define and Serialize an Object

We define a class Employee with an explicitly declared serialVersionUID and serialize an object of this class.

This will create a file employee.ser containing the serialized object.

Step 2: Deserialize Without Any Change

If we deserialize the object without modifying the class, it works fine.

Since the serialVersionUID in the serialized data matches the serialVersionUID of the class at runtime, deserialization is successful.

Scenario 2: serialVersionUID Does Not Match (Failure Case)

Step 1: Serialize an Object with serialVersionUID = 1L

We start by defining a class Employee with serialVersionUID = 1L and serialize an object.

  • The object is serialized and saved in employee.ser.

  • The serialVersionUID is 1L at this point.

Step 2: Modify the Class and Change serialVersionUID

Now, let's modify the Employee class by:

  • Changing serialVersionUID to 2L

  • Adding a new field department

Step 3: Deserialize the Old Object

Now, let's try to deserialize the old employee.ser file using the modified class.

Step 4: Runtime Error Due to Mismatch

When we run this code, we get the following exception:

Why Did This Happen?

  • The old serialized object (stored in employee.ser) was created when serialVersionUID = 1L.

  • The new class definition has serialVersionUID = 2L.

  • Java compares the two serialVersionUID values during deserialization. Since they do not match, Java rejects the deserialization process and throws InvalidClassException.

How to Fix This?

Solution 1: Keep serialVersionUID the Same

If we keep serialVersionUID = 1L in the new version of the class, deserialization will succeed. Even though the new field (department) was not in the original object, it will default to null.

Solution 2: Implement readObject() for Backward Compatibility

If we want the new department field to have a default value instead of null, we can use custom deserialization.

Now, when an old object is deserialized, it will have "Unknown" as the department value.

How to Maintain Compatibility?

Solution 1: Keep the Same serialVersionUID

If we keep serialVersionUID = 1L in the modified class, deserialization will work. Even though the new field department is missing in the old serialized object, Java will initialize it with its default value (null for String).

Deserialization succeeds, and the output will be:

Solution 2: Implement readObject() for Backward Compatibility

If we want full control over deserialization (e.g., setting default values for new fields), we can use readObject():

Now, even if we deserialize an older object, it will have "Unknown" as the default department.

Last updated