In Java, serialVersionUID is something like version control, assure both serialized and deserialized objects are using the compatible class.

For example, if an object saved into a file (Serialization) with serialVersionUID=1L, when we convert the file back to an object (Derialization), we must use the same serialVersionUID=1L, otherwise an InvalidClassException is thrown.

Exception in thread "main" java.io.InvalidClassException: com.favtuts.io.object.Address;

    local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2

1. POJO

Review a simple Address class, implements Serializable, and declared a serialVersionUID = 1L.

Address.java

package com.favtuts.io.object;

import java.io.Serializable;

public class Address implements Serializable {

    private static final long serialVersionUID = 1L;

    String street;
    String country;

    public Address(String street, String country) {
        this.street = street;
        this.country = country;
    }

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    @Override
    public String toString() {
        return "Address{" +
                "street='" + street + '\'' +
                ", country='" + country + '\'' +
                '}';
    }
}

2. Test SerialVersionUID.

2.1 Save an object into a file, and convert it back from the file to an object.

ObjectUtils.java

package com.favtuts.io.object;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class ObjectUtils {
    public static void main(String[] args) throws IOException, ClassNotFoundException {

        Address address = new Address("abc", "Malaysia");

        // object -> file
        try (FileOutputStream fos = new FileOutputStream("address.obj");
             ObjectOutputStream oos = new ObjectOutputStream(fos)) {
            oos.writeObject(address);
            oos.flush();
        }

        Address result = null;
        // file -> object
        try (FileInputStream fis = new FileInputStream("address.obj");
             ObjectInputStream ois = new ObjectInputStream(fis)) {
            result = (Address) ois.readObject();
        }

        System.out.println(result);

    }
}

Output

  Address{street='abc', country='Malaysia'}

2.2 Update the serialVersionUID to 99L.

Address.java

public class Address implements Serializable {

    private static final long serialVersionUID = 99L;

    String street;
    String country;
    //...

Rerun the deserialization process.

  Address result = null;
  // file -> object
  try (FileInputStream fis = new FileInputStream("address.obj");
       ObjectInputStream ois = new ObjectInputStream(fis)) {
      result = (Address) ois.readObject();
  }

  System.out.println(result);

Now, we hit InvalidClassException.

xception in thread "main" java.io.InvalidClassException: com.favtuts.io.object.Address; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 99
        at java.base/java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:689)
        at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2014)
        at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1864)
        at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2195)
        at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1681)
        at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:493)
        at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:451)
        at com.favtuts.io.object.ObjectUtils.main(ObjectUtils.java:25)

3. When should I update the serialVersionUID?

When we modify a class that will break the compatibility with the existing serialized object, for example, we update the current Address class, with some fields added and removed.

This new changed will failed all the existing serialized objects, then we should update the serialVersionUID.

Address.java

public class Address implements Serializable {

    private static final long serialVersionUID = 9999L;

    String line1;
    String line2;
    Country country;

    //String street;
    //String country;
}

4. Default serialVersionUID?

If no serialVersionUID is declared, JVM will use its algorithm to generate a default SerialVersionUID, check the algorithm here. The default serialVersionUID computation is highly sensitive to class details and may vary from different JVM implementation, and result in an unexpected InvalidClassExceptions during the deserialization process.

Review the following example:

Client / Server environment
– The client is using OpenJDK on Windows.
– The server is using GraalVM On Linux.

The client sends the serialized class with SerialVersionUID=1L to the server. The server may deserialize the class with a different serialVersionUID=99L, and throws InvalidClassExceptions. Since both are using different JVM implementation, it might generate a different SerialVersionUID on both sites.

There are many JVM implementation

Download Source Code

$ git clone https://github.com/favtuts/java-core-tutorials-examples

$ cd java-io/object

References

Leave a Reply

Your email address will not be published. Required fields are marked *