Usage of serialVersionUID in Java serialization

Java run-time associates with each serializable class a special version number known as serialVersionUID. This number is later used during deserialization process to verify if the application which performed the serialization and the application which is doing the deserialization have loaded and use compatible Java classes (with respect to serialization). If the receiver has loaded a class which has the same name but different serialVersionUID, the Java run-time will report a problem by throwing an exception of type InvalidClassException.

Generally speaking this kind of mechanism allows us to detect and report any compatibility problems during deserialization process as early as possible. If this check was missing, the deserialization process would likely finish without raising any exception but the created object would contain invalid data. This can easily lead to unpredictable behavior and can later cause errors which root cause is hard to track.

Defining serialVersionUID

A serializable class can specify its own serialVersionUID value explicitly by declaring a static, final field of name serialVersionUID and of type long:

private static final long serialVersionUID = 4038305141805077716L;

The access modifier does not really matter but private is commonly used.

If serial version is not explicitly declared, it will be automatically generated by compiler and stored in the class file. The compiler calculates default serialVersionUID value based on several aspects of the class like its name, its all public and protected members and so on.

The default serialVersionUID calculation may vary between compilers so it may give different results in different environments. To ensure a consistent serialVersionUID value, it is strongly recommended to explicitly declare its value.

Many IDE for Java have a feature which detects if the class should declare serialVersionUID and can report it as a warning. In case of the Eclipse the warning is following:

The serializable class Person does not declare a static final serialVersionUID field of type long

Adding serialVersionUID to existing classes

If you create a new class, you can choose any arbitrary value for its serialVersionUID because there are no serialized objects of this class yet stored anywhere.

However, if you already have an old class to which you would like to add explicit serial version value, you have be careful. The best idea is to choose a value which would be automatically generated by the compiler.

Oracle JDK provides a program serialver which shows the following basic window when started with -show option from command line:
serialver
When you enter a full name of the existing class and click Show button, it will output its default generated serialVersionUID below. You can just copy-paste this line into Java class file.

Additionally, Eclipse provides a nice context tool which can simplify adding serialVersionUID:

serialver-eclipse

Managing explicit serialVersionUID

The serialVersionUID represents a class version and has to be managed. If you make a change to the existing class which makes it incompatible with the previous version (e.g. changing base class, modifying the types of the fields or removing fields), it is also necessary to modify (e.g. increment) its serialVersionUID value.

On the other hand, adding new fields to the class does not necessarily make it incompatible. Additionally, you may manage to keep the class backward compatible by customizing serialization procedure using readObject() and writeObject() methods.

Example

Let’s consider a simple application serializing and deserializing objects of Person class:

package com.example.serialversionuid;

import java.io.Serializable;

public class Person implements Serializable {
    private static final long serialVersionUID = 4038305141805077716L;
	
    private String firstName;
    private String lastName;

    public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    @Override
    public String toString() {
        return "Person{" + "firstName=" + firstName + ", lastName=" + lastName + '}';
    }
    
}

with the main class:

package com.example.serialversionuid;

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

public class Main {
    
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        if (args.length != 2) {
            System.err.println("Usage: serialversionuid read|write FILE");
            return;
        }
        final String operation = args[0];
        final String file = args[1];
        
        
        if (operation.equals("read")) {
            try (ObjectInputStream is = new ObjectInputStream(new FileInputStream(file))) {
                Person person = (Person) is.readObject();
                System.out.println("Read person: " + person);
            }
        } else if (operation.equals("write")) {
            Person person = new Person("James", "Bond");
            try (ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(file))) {
                os.writeObject(person);
            }
        }
    }
    
}

When the serialVersionUID is unchanged we can serialize and deserialize objects of Person class without problems:

$ java -jar serialversionuid-1.0-SNAPSHOT.jar write data.ser
$ java -jar serialversionuid-1.0-SNAPSHOT.jar read data.ser
Read person: Person{firstName=James, lastName=Bond}

However, if you change serialVersionUID in Person class, rebuild the jar file and try to read data.ser file again, the InvalidClassException exception appears:

$ java -jar serialversionuid-1.0-SNAPSHOT.jar read data.ser
Exception in thread "main" java.io.InvalidClassException: com.example.serialversionuid.Person; local class incompatible: stream classdesc serialVersionUID = 4038305141805077716, local class serialVersionUID = 4038305141805077717
        at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:617)
        at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1622)
        at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1517)
        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1771)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)
        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
        at com.example.serialversionuid.Main.main(Main.java:22)

As you can easily notice, the Java run-time reports incompatibility between classes used to serialize data and deserialize it later.

Conclusion

Field serialVersionUID is often forgotten and ignored. In most cases not declaring it does not cause any problems. However, if your application exchanges serialized objects over network with another applications or stores serialized data on disk for long time, the importance of proper management of serialVersionUID becomes crucial.

The source code for the example is available at GitHub.

Advertisement

About Robert Piasecki

Husband and father, Java software developer, Linux and open-source fan.
This entry was posted in Java and tagged . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.