Guide to Java SerialVersionUID

Java serialization is the process of converting an object into a stream of bytes so we can do stuff like store it on disk or send it over the network. Deserialization is the reverse process – converting a stream of bytes into an object in memory.

During serialization, the java runtime associates a version number with each serializable class. This number is called serialVersionUID, which is used during deserialization to verify that the sender and receiver of a serialized object have loaded classes for that object that are compatible with respect to serialization. If the receiver has loaded a class for the object that has a different serialVersionUID than that of the corresponding sender’s class, then deserialization will result in an InvalidClassException.

1. Java SerialVersionUID Syntax

A Serializable class can declare its own SerialVersionUID explicitly by declaring a field named “serialVersionUID” that must be static, final, and of type long.

private static final long serialVersionUID = 1L;

Here the serialVersionUID represents the class version, and we should increment it if the current version of the class is modified such that it is no longer backward compatible with its previous version.

Serialization-deserialization-demo

2. Java Serialization and Deserialization Examples

Let us see an example of how a class is serialized and then deserialized.

import java.io.*;
import java.util.logging.Logger;

public class DemoClass implements java.io.Serializable {

	private static final long serialVersionUID = 4L;			//Default serial version uid
	private static final String fileName = "DemoClassBytes.ser"; //Any random name
	private static final Logger logger = Logger.getLogger("");
	//Few data fields
	//Able to serialize
	private static String staticVariable;
	private int intVariable;

	//Not able to serialize
	transient private String transientVariable = "this is a transient instance field";
	private Thread threadClass;

	public static void main(String[] args) throws IOException, ClassNotFoundException
	{
		//Serialization

	    DemoClass test = new DemoClass();
	    test.intVariable = 1;
	    staticVariable = "this is a static variable";
	    writeOut(test);
	    System.out.println("DemoClass to be saved: " + test);

	    //De-serialization

	    System.out.println("DemoClass deserialized: " + readIn());
	}

	private static Object readIn() throws IOException, ClassNotFoundException {
	    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File(fileName)));
	    return ois.readObject();
	}

	private static void writeOut(java.io.Serializable obj) throws IOException {
	    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File(fileName)));
	    oos.writeObject(obj);
	    oos.close();
	}

	@Override public String toString() {
	    return "DemoClass: final static fileName=" + fileName + ", final static logger=" + logger
	            + ", non-final static staticVariable=" + staticVariable + ", instance intVariable=" + intVariable
	            + ", transient instance transientVariable=" + transientVariable + ", non-serializable instance field threadClass:=" + threadClass;
	}
}

Program Output.

DemoClass to be saved: DemoClass:
final static fileName=DemoClassBytes.ser,
final static logger=java.util.logging.LogManager$RootLogger@1d99a4d,
non-final static staticVariable=this is a static variable,
instance intVariable=1,
transient instance transientVariable=this is a transient instance field,
non-serializable instance field threadClass:=null

//Execute readIn() function from a separate main() method
//to get given below output correctly. It will flush out the static fields.

DemoClass deserialized: DemoClass:
final static fileName=DemoClassBytes.ser,
final static logger=java.util.logging.LogManager$RootLogger@cd2c3c,
non-final static staticVariable=null,
instance intVariable=1,
transient instance transientVariable=null,
non-serializable instance field threadClass:=null

If a serializable class does not explicitly declare a serialVersionUID, then the serialization runtime will calculate a default serialVersionUID value for that class based on various aspects of the class.

3. How to Generate SerialVersionUID

Joshua Bloch says in Effective Java that the automatically generated UID is generated based on a class name, implemented interfaces, and all public and protected members. Changing any of these in any way will change the serialVersionUID.

However, it is strongly recommended that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations and can produce different serialVersionUID in different environments. This can result in unexpected InvalidClassException during deserialization.

Therefore, to guarantee a consistent serialVersionUID value across different java compiler implementations, a serializable class must declare an explicit serialVersionUID value. It is also strongly advised that explicit serialVersionUID declarations use the private modifier in serialVersionUID where possible, since such declarations apply only to the immediately declaring class.

Also, note that serialVersionUID field is not helpful as an inherited member.

Based on my short career, I can say that storing serialized data for a long time span [spatial serialization] is not a very common usecase. It is far more common to use the serialization mechanism to temporarily write data [temporal serialization] to for instance a cache or send it over the network to another program to utilize the information.

In such cases, we are not interested in maintaining backward compatibility. We are only concerned with making sure that the code bases which are communicating on the network, indeed have the same version of relevant classes. In order to facilitate such a check, we must maintain the serialVersionUID just as it is and don’t change it. Also, do not forget to update it when making incompatible changes to your classes, on both sides of applications on the network.

4. Java Classes without SerialVersionUID

It is not the situation that we ever want to face. But, it’s reality and sometimes it happens (should i say rarely?). If we need to change such a class in an incompatible way but want to maintain serialization/deserialization capability with the old version of the class, we can use the JDK tool “serialver“. This tool generates the serialVersionUID on the old class, and explicitly set that on the new class. Do not forget to to implement readObject() and writeObject() methods because the built-in deserialization mechanism (in.defaultReadObject()) will refuse to deserialize from old versions of the data.

If we define our own readObject() function which can read back old data. This custom code should check the serialVersionUID in order to know which version the data is in and decide how to deserialize it. This versioning technique is helpful if we store serialized data that survives several versions of your code.

Read more : Java serialization compatible and incompatible changes

5. Summary

  • The transient and static fields are ignored in serialization. After deserialization transient fields and non-final static fields will be null.

    final and static fields still have values since they are part of the class data.

  • ObjectOutputStream.writeObject(obj) and ObjectInputStream.readObject() are used in serialization and deserialization.
  • During serialization, we need to handle IOException; during deserialization, we need to handle IOException and ClassNotFoundException. So the deserialized class type must be in the classpath.
  • Uninitialized non-serializable, non-transient instance fields are tolerated.

    When adding “private Thread th;“, no error in serializable. However, “private Thread threadClass = new Thread();” will cause exception:

Exception in thread "main" java.io.NotSerializableException: java.lang.Thread
at java.io.ObjectOutputStream.writeObject0(Unknown Source)
at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source)
at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
at java.io.ObjectOutputStream.writeObject0(Unknown Source)
at java.io.ObjectOutputStream.writeObject(Unknown Source)
at com.howtodoinjava.demo.serialization.DemoClass.writeOut(DemoClass.java:42)
at com.howtodoinjava.demo.serialization.DemoClass.main(DemoClass.java:27)
  • Serialization and deserialization can be used for copying and cloning objects. It is slower than a regular clone but can produce a deep copy very quickly.
  • If I need to serialize a Serializable class Employee, but one of its superclasses is not Serializable, can Employee class still be serialized and deserialized? The answer is yes, provided that the non-serializable super-class has a no-arg constructor, which is invoked at deserialization to initialize that super-class.
  • We must be careful while modifying a class and implementing java.io.Serializable. If the class does not contain a serialVersionUID field, its serialVersionUID will be automatically generated by the compiler.

    Different compilers, or different versions of the same compiler, will generate potentially different values.

  • Computation of serialVersionUID is based not only on fields but also on other aspects of the class like implement clauses, constructors, etc. So the best practice is explicitly declaring a serialVersionUID field to maintain backward compatibility. If we need to modify the serializable class substantially and expect it to be incompatible with previous versions, then we need to increment serialVersionUID to avoid mixing different versions.

Happy Learning !!

Comments

Subscribe
Notify of
guest
25 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode