Рубрики
Без рубрики

Что такое serialVersionUID?

Быстрый и практический пример использования serialVersionUID в Java.

Автор оригинала: baeldung.

1. Обзор

Атрибут serialVersionUID – это идентификатор, который используется для сериализации/десериализации объекта класса Serializable .

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

2. UID серийной версии

Проще говоря, мы используем serialVersionUID атрибут для запоминания версий Сериализуемый класс для проверки совместимости загруженного класса и сериализованного объекта.

Атрибуты serialVersionUID разных классов независимы. Поэтому нет необходимости, чтобы разные классы имели уникальные значения.

Далее давайте узнаем, как использовать serialVersionUID на некоторых примерах.

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

public class AppleProduct implements Serializable {

    private static final long serialVersionUID = 1234567L;

    public String headphonePort;
    public String thunderboltPort;
}

Далее нам понадобятся два служебных класса: один для сериализации продукта Apple объекта в Строку, и другой для десериализации объекта из этой Строки:

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 , который сохраняет (сериализует) Продукт Apple объект в Строку экземпляр e, кодируя байты с помощью Base64 .

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

Полученный результат должен быть похож на этот:

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

Теперь давайте изменим константу serialVersionUID в AppleProduct.java, и попытка десериализации объекта продукта Apple из той же строки, созданной ранее. Повторный запуск DeserializationUtility.java должен генерировать этот вывод.

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)

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

Если serialVersionUID не указан в классе Serializable , JVM автоматически сгенерирует его. Однако рекомендуется предоставить значение serialVersionUID и обновить его после изменений в классе, чтобы мы могли контролировать процесс сериализации/десериализации . Мы подробнее рассмотрим это в более позднем разделе.

3. Совместимые изменения

Допустим, нам нужно добавить новое поле порт lightning в наш существующий Продукт Apple класс:

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

Поскольку мы просто добавляем новое поле, никаких изменений в serialVersionUID не потребуется . Это связано с тем, что в процессе десериализации null будет присвоено значение по умолчанию для поля lightning Port |.

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

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

Теперь, когда мы повторно запустим утилиту десериализации класса, мы увидим вывод, похожий на:

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

4. Серийная версия По Умолчанию

Если мы не определим serialVersionUID состояние для a Сериализуемый класс, то Java определит его на основе некоторых свойств самого класса, таких как имя класса, поля экземпляра и так далее.

Давайте определим простой Сериализуемый класс:

public class DefaultSerial implements Serializable {
}

Если мы сериализуем экземпляр этого класса следующим образом:

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

Это выведет дайджест Base64 сериализованного двоичного файла:

rO0ABXNyACpjb20uYmFlbGR1bmcuZGVzZXJpYWxpemF0aW9uLkRlZmF1bHRTZXJpYWx9iVz3Lz/mdAIAAHhw

Как и раньше, мы должны иметь возможность десериализовать этот экземпляр из дайджеста:

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

Однако некоторые изменения в этом классе могут нарушить совместимость сериализации. Например, если мы добавим поле private в этот класс:

public class DefaultSerial implements Serializable {
    private String name;
}

А затем попробуйте десериализовать тот же дайджест Base64 в экземпляр класса, мы получим исключение InvalidClassException:

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

Из-за такого рода нежелательной несовместимости всегда рекомендуется объявлять serialVersionUID в Сериализуемых классах. Таким образом, мы можем сохранить или развить версию по мере развития самого класса.

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

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

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