Java Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

1. Overview

Simply put, the serialVersionUID is a unique identifier for Serializable classes.

This is used during the deserialization of an object, to ensure that a loaded class is compatible with the serialized object. If no matching class is found, an InvalidClassException is thrown.

2. Serial Version UID

Let's start by creating a serializable class, and declare a serialVersionUID identifier:

public class AppleProduct implements Serializable {

    private static final long serialVersionUID = 1234567L;

    public String headphonePort;
    public String thunderboltPort;
}

Next, we'll need two utility classes: one to serialize an AppleProduct object into a String, and another to deserialize the object from that String:

public class SerializationUtility {

    public static void main(String[] args) {
        AppleProduct macBook = new AppleProduct();
        macBook.headphonePort = "headphonePort2020";
        macBook.thunderboltPort = "thunderboltPort2020";

        String serializedObj = serializeObjectToString(macBook);
 
        System.out.println("Serialized AppleProduct object to string:");
        System.out.println(serializedObj);
    }

    public static String serializeObjectToString(Serializable o) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(o);
        oos.close();
        
        return Base64.getEncoder().encodeToString(baos.toByteArray());
    }
}
public class DeserializationUtility {
 
    public static void main(String[] args) {
 
        String serializedObj = ... // ommited for clarity
        System.out.println(
          "Deserializing AppleProduct...");
 
        AppleProduct deserializedObj = (AppleProduct) deSerializeObjectFromString(
          serializedObj);
 
        System.out.println(
          "Headphone port of AppleProduct:"
            + deserializedObj.getHeadphonePort());
        System.out.println(
          "Thunderbolt port of AppleProduct:"
           + deserializedObj.getThunderboltPort());
    }
 
    public static Object deSerializeObjectFromString(String s)
      throws IOException, ClassNotFoundException {
  
        byte[] data = Base64.getDecoder().decode(s);
        ObjectInputStream ois = new ObjectInputStream(
          new ByteArrayInputStream(data));
        Object o = ois.readObject();
        ois.close();
        return o;
    }
}

We begin by running SerializationUtility.java, which saves (serializes) the AppleProduct object into a String instance, encoding the bytes using Base64.

Then, using that String as an argument for the deserialization method, we run DeserializationUtility.java, which reassembles (deserializes) the AppleProduct object from the given String.

The output generated should be similar to this:

Serialized AppleProduct object to string:
rO0ABXNyACljb20uYmFlbGR1bmcuZGVzZXJpYWxpemF0aW9uLkFwcGxlUHJvZHVjdAAAAAAAEta
HAgADTAANaGVhZHBob25lUG9ydHQAEkxqYXZhL2xhbmcvU3RyaW5nO0wADmxpZ2h0ZW5pbmdQb3
J0cQB+AAFMAA90aHVuZGVyYm9sdFBvcnRxAH4AAXhwdAARaGVhZHBob25lUG9ydDIwMjBwdAATd
Gh1bmRlcmJvbHRQb3J0MjAyMA==
Deserializing AppleProduct...
Headphone port of AppleProduct:headphonePort2020
Thunderbolt port of AppleProduct:thunderboltPort2020

Now, let's modify the serialVersionUID constant in AppleProduct.java, and reattempt to deserialize the AppleProduct object from the same String produced earlier. Re-running DeserializationUtility.java should generate this output.

Deserializing AppleProduct...
Exception in thread "main" java.io.InvalidClassException: com.baeldung.deserialization.AppleProduct; local class incompatible: stream classdesc serialVersionUID = 1234567, local class serialVersionUID = 7654321
	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1630)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1521)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1781)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)
	at com.baeldung.deserialization.DeserializationUtility.deSerializeObjectFromString(DeserializationUtility.java:24)
	at com.baeldung.deserialization.DeserializationUtility.main(DeserializationUtility.java:15)

By changing the serialVersionUID of the class, we modified its version/state. As a result, no compatible classes were found during deserialization, and an InvalidClassException was thrown.

3. Compatible Changes

Let's say we need to add a new field lightningPort to our existing AppleProduct class:

public class AppleProduct implements Serializable {
//...
    public String lightningPort;
}

Since we are just adding a new field, no change in the serialVersionUID will be required. This is because, during the deserialization process, null will be assigned as the default value for the lightningPort field.

Let's modify our DeserializationUtility class to print the value of this new field:

System.out.println("LightningPort port of AppleProduct:"
  + deserializedObj.getLightningPort());

Now, when we rerun the DeserializationUtility class, we will see output similar to:

Deserializing AppleProduct...
Headphone port of AppleProduct:headphonePort2020
Thunderbolt port of AppleProduct:thunderboltPort2020
Lightning port of AppleProduct:null

4. Default Serial Version

If we don't define a serialVersionUID state for a Serializable class, then Java will define one based on some properties of the class itself such as the class name, instance fields, and so on.

Let's define a simple Serializable class:

public class DefaultSerial implements Serializable {
}

If we serialize an instance of this class like the following:

DefaultSerial instance = new DefaultSerial();
System.out.println(SerializationUtility.serializeObjectToString(instance));

This will print the Base64 digest of the serialized binary:

rO0ABXNyACpjb20uYmFlbGR1bmcuZGVzZXJpYWxpemF0aW9uLkRlZmF1bHRTZXJpYWx9iVz3Lz/mdAIAAHhw

Just like before, we should be able to deserialize this instance from the digest:

String digest = "rO0ABXNyACpjb20uYmFlbGR1bmcuZGVzZXJpY" 
  + "WxpemF0aW9uLkRlZmF1bHRTZXJpYWx9iVz3Lz/mdAIAAHhw";
DefaultSerial instance = (DefaultSerial) DeserializationUtility.deSerializeObjectFromString(digest);

However, some changes to this class may break the serialization compatibility. For instance, if we add a private field to this class:

public class DefaultSerial implements Serializable {
    private String name;
}

And then try to deserialize the same Base64 digest to a class instance, we'll get an InvalidClassException:

Exception in thread "main" java.io.InvalidClassException: 
  com.baeldung.deserialization.DefaultSerial; local class incompatible: 
  stream classdesc serialVersionUID = 9045863543269746292, 
  local class serialVersionUID = -2692722436255640434

Because of this sort of unwanted incompatibility, it's always a good idea to declare a serialVersionUID in Serializable classes. This way we can keep or evolve the version as the class itself evolves.

5. Conclusion

In this quick article, we demonstrated the use of the serialVersionUID constant to facilitate versioning of serialized data.

As always, the code samples used throughout this article can be found over on GitHub.

Java bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
4 Comments
Oldest
Newest
Inline Feedbacks
View all comments
Mika Ropponen
Mika Ropponen
3 years ago

If you let IDE generate the serialVersionUID value, it’s always 1L for every class and works fine. How is this possible, if the serialVersionUID has to be unique?

Grzegorz Piwowarek
Grzegorz Piwowarek
3 years ago
Reply to  Mika Ropponen

Pairs class-serialVersionUID need to be unique. SerialVersionUID itself does not need to be unique globally.

Sam Mizanin
Sam Mizanin
3 years ago

Well what happens if I implement Serializable but don’t have a serialVersionUID? If I serialize an object, put a new instance variable in the class and try to deserialize the object… will it work? One of main aspects of having a serialVersionUID is to have a common version id for a class even if the class blueprint changes which needs to be mentioned in the article.

Grzegorz Piwowarek
Grzegorz Piwowarek
3 years ago
Reply to  Sam Mizanin

If you do not define your own serialVersionUID, you will get a warning and JVM will generate one automatically. Thanks for the suggestion, we will update the article 🙂

Comments are closed on this article!