Сериализация — это средство сохранения объекта. Он широко используется в сетевой передаче, RMI и других сценариях. класс путем реализацииjava.io.Serializable
интерфейс, чтобы включить его возможности сериализации.
В моем блоге на самом деле много статей о сериализации.Друзья, которые недостаточно разбираются в основах сериализации, могут обратиться к следующим статьям:
Сериализация и десериализация объектов Java Углубленный анализ сериализации и десериализации JavaЭти вещи о синглтонах и сериализации
В этих статьях я представил классы и интерфейсы, участвующие в сериализации, как настроить стратегию сериализации, временные ключевые слова и взаимосвязь между сериализацией и т. д., а также глубоко изучил исходный код реализации сериализации, изучив ArrayList.serialized. А также расширили анализ влияния сериализации на синглтоны.
Однако есть еще один пункт знаний, который не был введен, а именно оserialVersionUID
. Для чего именно используется это поле? Что делать, если он не установлен? Почему в Руководстве по разработке Java для Alibaba есть следующее положение:
Прежде чем начать введение в эту статью, давайте кратко представим некоторые знания, связанные с сериализацией.Содержимое взято из трех ссылок на статьи в начале статьи.
Сериализуемый и экстернализируемый
Классы Java реализованыjava.io.Serializable
интерфейс, чтобы включить его возможности сериализации.Классы, которые не реализуют этот интерфейс, не смогут сериализовать или десериализовать.Все подтипы сериализуемого класса сами сериализуемы.
Если читатель виделSerializable
исходный код, вы обнаружите, что это просто пустой интерфейс, в котором ничего нет.Интерфейс Serializable не имеет методов или полей и используется только для идентификации сериализуемой семантики.Однако, если класс не реализует этот интерфейс и хочет сериализоваться, он выдастjava.io.NotSerializableException
аномальный.
Как это гарантирует, что только методы, реализующие интерфейс, могут быть сериализованы и десериализованы?
Причина в том, что в процессе сериализации выполняется следующий код:
if (obj instanceof String) { writeString((String) obj, unshared);} else if (cl.isArray()) { writeArray(obj, desc, unshared);} else if (obj instanceof Enum) { writeEnum((Enum<?>) obj, desc, unshared);} else if (obj instanceof Serializable) { writeOrdinaryObject(obj, desc, unshared);} else { if (extendedDebugInfo) { throw new NotSerializableException( cl.getName() + "\n" + debugInfoStack.toString()); } else { throw new NotSerializableException(cl.getName()); }}
скопировать код
При сериализации он будет определять, является ли сериализуемый классEnum
,Array
иSerializable
типа, если это ни то, ни другое, кидайте сразуNotSerializableException
.
Java также предоставляетExternalizable
интерфейс, который также может быть реализован для предоставления возможностей сериализации.
Externalizable
унаследовано отSerializable
, в этом интерфейсе определены два абстрактных метода:writeExternal()
иreadExternal()
.
когда используешьExternalizable
Когда интерфейс используется для сериализации и десериализации, разработчикам необходимо его переписать.writeExternal()
иreadExternal()
метод. В противном случае значения всех переменных станут значениями по умолчанию.
transient
transient
Функция ключевого слова – управлять сериализацией переменных. Добавление этого ключевого слова перед объявлением переменной может предотвратить сериализацию переменной в файл. После десериализацииtransient
Значение переменной устанавливается равным начальному значению, например 0 для типа int и null для типа объекта.
настраиваемая стратегия сериализации
Во время сериализации, если сериализованный класс определяетwriteObject
иreadObject
метод, виртуальная машина пытается вызвать метод в классе объектаwriteObject
иreadObject
метод пользовательской сериализации и десериализации.
Если такого метода нет, вызов по умолчаниюObjectOutputStream
изdefaultWriteObject
метод иObjectInputStream
изdefaultReadObject
метод.
определяемые пользователемwriteObject
иreadObject
Этот метод позволяет пользователю управлять процессом сериализации, например, динамически изменять сериализованное значение во время процесса сериализации.
Поэтому, когда вам нужно определить стратегию сериализации для некоторых специальных полей, вы можете рассмотреть возможность использования временной модификации и переписать ее самостоятельно.writeObject
иreadObject
такие методы, какjava.util.ArrayList
Есть такая реализация.
Вышеизложенное — это знания, которые некоторым читателям необходимо освоить и связанные с сериализацией.
Случайным образом находим в Java несколько классов, реализующих интерфейс сериализации, таких как String, Integer и т. д., можем найти деталь, то есть помимо реализации этих классовSerializable
Кроме того, он также определяетserialVersionUID
Итак, что именноserialVersionUID
Шерстяная ткань? Зачем задавать такое поле?
Сериализация — это процесс преобразования информации о состоянии объекта в форму, которую можно хранить или передавать. Все мы знаем, что объекты Java хранятся в куче памяти JVM, то есть, если кучи JVM не существует, объекты также исчезнут.
Сериализация предоставляет решение, позволяющее сохранять объекты, даже когда JVM не работает. Так же, как диск U, который мы обычно используем. Сериализация объектов Java в форму, которую можно хранить или передавать (например, в двоичный поток), например в файл. Таким образом, когда объект снова понадобится, двоичный поток считывается из файла, а объект десериализуется из двоичного потока.
Разрешает ли виртуальная машина десериализацию, зависит не только от согласованности пути к классам и кода функции, но также очень важным моментом является то, согласованы ли идентификаторы сериализации двух классов., этот так называемый идентификатор сериализации — это то, что мы определяем в кодеserialVersionUID
.
Давайте возьмем пример и посмотрим, еслиserialVersionUID
Что произойдет, если его изменить?
public class SerializableDemo1 { public static void main(String[] args) { //Initializes The Object User1 user = new User1(); user.setName("hollis"); //Write Obj to File ObjectOutputStream oos = null; try { oos = new ObjectOutputStream(new FileOutputStream("tempFile")); oos.writeObject(user); } catch (IOException e) { e.printStackTrace(); } finally { IOUtils.closeQuietly(oos); } }}class User1 implements Serializable { private static final long serialVersionUID = 1L; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
скопировать код
Сначала мы выполняем приведенный выше код, чтобы записать объект User1 в файл. Затем мы модифицируем класс User1 и поместимserialVersionUID
Значение изменяется на2L
.
class User1 implements Serializable { private static final long serialVersionUID = 2L; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; }}
скопировать код
Затем выполните следующий код, чтобы десериализовать объект в файле:
public class SerializableDemo2 { public static void main(String[] args) { //Read Obj from File File file = new File("tempFile"); ObjectInputStream ois = null; try { ois = new ObjectInputStream(new FileInputStream(file)); User1 newUser = (User1) ois.readObject(); System.out.println(newUser); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { IOUtils.closeQuietly(ois); try { FileUtils.forceDelete(file); } catch (IOException e) { e.printStackTrace(); } } }}
скопировать код
Результат выполнения следующий:
java.io.InvalidClassException: com.hollis.User1; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
скопировать код
Можно обнаружить, что приведенный выше код выдаетjava.io.InvalidClassException
, и указываетserialVersionUID
непоследовательный.
Это связано с тем, что при десериализации JVMserialVersionUID
с локальным соответствующим классом сущностейserialVersionUID
Для сравнения, если они совпадают, то считаются непротиворечивыми и могут быть десериализованы, в противном случае произойдет исключение несогласованных сериализованных версий, чтоInvalidCastException
.
Это же оговорено в "Руководстве по разработке Java для Alibaba", в апгрейде совместимости при модификации класса не модифицироватьserialVersionUID
причина.если только две версии полностью несовместимы. так,serialVersionUID
На самом деле, это проверка согласованности версий.
Если читателю интересно, вы можете взглянуть на код JDK каждой версии, а также на коды классов, совместимых с предыдущими версиями.serialVersionUID
не изменился. например, строкаserialVersionUID
Всегда-6849794470754667710L
.
Однако автор считает, что на самом деле эта спецификация может быть более строгой, то есть в ней оговариваются:
Если класс реализуетSerializable
интерфейс, необходимо вручную добавитьprivate static final long serialVersionUID
переменная и установить начальное значение.
Если мы не явно не определяем один в классеserialVersionUID
Если да, то посмотрите, что произойдет.
Попробуйте изменить демонстрационный код выше, сначала используйте следующий класс для определения объекта, который не определен в этом классе.serialVersionUID
, запишите его в файл.
class User1 implements Serializable { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
скопировать код
Затем мы модифицируем класс User1, чтобы добавить к нему свойство. Попытка прочитать его из файла и десериализовать.
class User1 implements Serializable { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
скопировать код
Результаты:
java.io.InvalidClassException: com.hollis.User1; local class incompatible: stream classdesc serialVersionUID = -2986778152837257883, local class serialVersionUID = 7961728318907695402
скопировать код
Так же, метаниеInvalidClassException
, и указать дваserialVersionUID
разные, соответственно-2986778152837257883
и7961728318907695402
.
Отсюда видно, что система добавилаserialVersionUID
.
Итак, как только класс реализуетSerializable
, рекомендуется четко определитьserialVersionUID
. В противном случае при модификации класса возникнет исключение.
serialVersionUID
Существует два метода генерации отображения: Один из них — 1 л по умолчанию, например:
private static final long serialVersionUID = 1L;
скопировать код
Другой способ — создать 64-битное хэш-поле на основе имени класса, имени интерфейса, метода-члена и атрибута, например:
private static final long serialVersionUID = xxxxL;
скопировать код
Последний метод можно сгенерировать с помощью IDE, которая будет представлена позже.
Принцип, лежащий в основеЗная это, чтобы узнать почему, давайте посмотрим на исходный код и проанализируем, почемуserialVersionUID
Выбросит исключение при изменении? При отсутствии явного определения по умолчаниюserialVersionUID
Как это произошло?
Чтобы упростить размер кода, цепочка вызовов десериализации выглядит следующим образом:
ObjectInputStream.readObject -> readObject0 -> readOrdinaryObject -> readClassDesc -> readNonProxyDesc -> ObjectStreamClass.initNonProxy
скопировать код
существуетinitNonProxy
, код ключа выглядит следующим образом:
Во время десериализацииserialVersionUID
После выполнения сравнения, если обнаруживается, что они не равны, сразу выбрасывается исключение.
Посмотрите поближеgetSerialVersionUID
метод:
public long getSerialVersionUID() { // REMIND: synchronize instead of relying on volatile? if (suid == null) { suid = AccessController.doPrivileged( new PrivilegedAction<Long>() { public Long run() { return computeDefaultSUID(cl); } } ); } return suid.longValue();}
скопировать код
в неопределенномserialVersionUID
В то время его будет называтьсяcomputeDefaultSUID
метод, который генерирует значение по умолчаниюserialVersionUID
.
Это также находит корень двух вышеупомянутых проблем.На самом деле, в коде выполняется строгая проверка, и serialVersionUID автоматически генерируется, когда он не определен.
ИДЕЯ СоветыЧтобы убедиться, что мы не забыли определитьserialVersionUID
, вы можете настроить конфигурацию Intellij IDEA, в реализацииSerializable
После интерфейса, если он не определенserialVersionUID
Если это так, IDEA (например, eclipse) предложит:
И может сгенерировать его одним щелчком мыши:
Конечно, эта конфигурация не действует по умолчанию, вам нужно установить ее вручную в IDEA:
В месте с пометкой 3 на рисунке (Конфигурация класса Serializable без serialVersionUID) отметьте его и сохраните.
СуммироватьserialVersionUID
Он используется для проверки согласованности версий. такПри обновлении совместимости не меняйте классserialVersionUID
значение .
В частности, поскольку название этой статьи не полностью выражает всего содержания этой статьи, здесь еще раз подчеркнуто: serialVersionUIDПоскольку это необходимо для проверки согласованности версий, при обновлении версий (обновлениях, несовместимых с совместимостью) не забудьте изменить значение этого поля, чтобы избежать путаницы с сериализацией.
Если класс реализует интерфейс Serializable, обязательно определитеserialVersionUID
, иначе возникнет исключение. Вы можете установить его в IDE, пусть он подскажет, а может быстро сгенерировать в один кликserialVersionUID
.
Причина, по которой возникает исключение, заключается в том, что процесс десериализации действительно проверял, и если он явно не определен, то автоматически генерирует имя класса и атрибуты.
Статья в публичном аккаунте Hollis уполномочила
В последний месяц 2018 года у Hollis's Knowledge Planet действует ограниченная по времени скидка.Глубокое понимание параллельного программирования на Java: что такое безопасность потоков?Добро пожаловать присоединиться.
Столкнувшись с проблемой Java 197: почему символы искажены?
Road to God Issue 015: Углубленное изучение IO в Java
Глубокое погружение в проблему параллелизма 004: несколько способов реализации потоков
- ЕЩЕ | Другие интересные статьи -
-
Одноразовая производственная практика оптимизации ЦП 100% устранения неполадок
-
Случайный разговор: как объяснить балансировку нагрузки своей девушке
-
[Галантерейные товары] Краткое изложение некоторых часто используемых навыков алгоритма
Если вам нравится эта статья.
Пожалуйста, нажмите и удерживайте QR-код, чтобы подписаться на Холлис
Пересылка круга друзей - самая большая поддержка для меня.