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 .