Что такое serialVersionUID?

Что такое serialVersionUID?

1. обзор

Проще говоря,the serialVersionUID is a unique identifier for Serializable classes.

Это используется во время десериализации объекта, чтобы гарантировать, что загруженный класс совместим с сериализованным объектом. Если соответствующий класс не найден, выдаетсяInvalidClassException.

2. пример

Начнем с создания сериализуемого класса и объявим идентификаторserialVersionUID:

public class AppleProduct implements Serializable {

    private static final long serialVersionUID = 1234567L;

    public String headphonePort;
    public String thunderboltPort;
}

Затем нам понадобятся два служебных класса: один для сериализации объектаAppleProduct вString,, а другой для десериализации объекта из этого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;
    }
}

Мы начинаем с запускаSerializationUtility.java, который сохраняет (сериализует) объектAppleProduct в экземплярStringe,, кодируя байты с использованиемBase64.

Затем, используя этотString в качестве аргумента для метода десериализации, мы запускаемDeserializationUtility.java,, который повторно собирает (десериализует) объектAppleProduct из заданногоString.

Сгенерированный вывод должен быть похож на это:

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 serialVersionUIDconstant in AppleProduct.java, and reattempt to deserialize объектAppleProduct из той же строки, созданной ранее. Повторный запускDeserializationUtility.java должен сгенерировать этот вывод.

Deserializing AppleProduct...
Exception in thread "main" java.io.InvalidClassException: com.example.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.example.deserialization.DeserializationUtility.deSerializeObjectFromString(DeserializationUtility.java:24)
    at com.example.deserialization.DeserializationUtility.main(DeserializationUtility.java:15)

ИзменивserialVersionUID класса, мы изменили его версию / состояние. В результате во время десериализации не было найдено совместимых классов, и был выданInvalidClassException.

3. Пример - добавление нового поля в существующий класс

Допустим, нам нужно добавить новое полеlightningPort к нашему существующему классуAppleProduct:

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

Поскольку мы просто добавляем новое поле,no change in the serialVersionUID will be required. Это потому, чтоduring the deserialization process, null will be assigned as the default value for the lightningPort field.

Давайте изменим наш классDeserializationUtility, чтобы вывести значение этого нового поля:

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

Теперь, когда мы перезапустим классDeserializationUtility, мы увидим результат, аналогичный следующему:

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

4. Заключение

В этой быстрой статье мы продемонстрировали использование константыserialVersionUID для облегчения управления версиями сериализованных данных.

Как всегда, примеры кода, используемые в этой статье, можно найти вover on GitHub.