Что такое serialVersionUID и почему я должен его использовать?

2509

Eclipse выдает предупреждения, когда отсутствует serialVersionUID.

Сериализуемый класс Foo не объявляет статический окончательный Поле serialVersionUID типа long

Что такое serialVersionUID и почему это важно? Пожалуйста, покажите пример, где отсутствующий serialVersionUID вызовет проблему.

Теги:
serialization
serialversionuid

24 ответа

2021
Лучший ответ

Документы для java.io.Serializable, вероятно, примерно так же хорошо, как объяснение:

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

ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

Если сериализуемый класс явно не объявляет serialVersionUID, тогда среда выполнения сериализации вычислит значение serialVersionUID умолчанию для этого класса на основе различных аспектов класса, как описано в Спецификации сериализации объектов Java (TM). Однако настоятельно рекомендуется, чтобы все сериализуемые классы явно объявляли значения serialVersionUID, так как вычисление serialVersionUID по умолчанию очень чувствительно к деталям класса, которые могут различаться в зависимости от реализаций компилятора, и, следовательно, могут привести к неожиданным InvalidClassExceptions во время десериализации. Поэтому, чтобы гарантировать согласованное значение serialVersionUID в различных реализациях Java-компилятора, сериализуемый класс должен объявить явное значение serialVersionUID. Также настоятельно рекомендуется, чтобы явные объявления serialVersionUID использовали модификатор private, где это возможно, поскольку такие объявления применяются только к немедленно объявленным полям класса. serialVersionUID не используются в качестве унаследованных членов.

  • 284
    Итак, по сути, вы говорите, что, если пользователь не понимает весь вышеупомянутый материал, указанный пользователь не должен беспокоиться о сериализации? Я полагаю, вы ответили "как?" вместо того, чтобы объяснять «почему?». Я, например, не понимаю, почему я должен беспокоиться о SerializableVersionUID.
  • 313
    Причина есть во втором абзаце: если вы не укажете явным образом serialVersionUID, значение будет сгенерировано автоматически - но это хрупко, потому что это зависит от реализации компилятора.
Показать ещё 20 комментариев
429

Если вы сериализуете только потому, что вам нужно сериализовать для реализации (кто волнует, если вы сериализуете для HTTPSession, например... если он хранится или нет, вам, вероятно, не нужно де-сериализовать form object), тогда вы можете игнорировать это.

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

В большинстве случаев вы, вероятно, не будете использовать сериализацию напрямую. Если это так, создайте сериализуемый uid по умолчанию, щелкнув опцию быстрого исправления и не беспокойтесь об этом.

  • 71
    Я бы сказал, что если вы не используете сериализацию для постоянного хранения, вы должны использовать @SuppressWarnings вместо добавления значения. Он меньше загромождает класс и сохраняет работоспособность механизма serialVersionUID, чтобы защитить вас от несовместимых изменений.
  • 21
    Я не вижу, как добавление одной строки (аннотация @SuppressWarnings) в отличие от другой строки (сериализуемый идентификатор) «меньше загромождает класс». И если вы не используете сериализацию для постоянного хранения, почему бы вам просто не использовать «1»? В любом случае вас не заботит автоматически созданный идентификатор.
Показать ещё 9 комментариев
288

Я не могу упустить эту возможность, чтобы подключить книгу Джоша Блоха Эффективная Java (2-е издание). Глава 11 является незаменимым ресурсом для сериализации Java.

Per Josh, автоматически созданный UID создается на основе имени класса, реализованных интерфейсов и всех открытых и защищенных членов. Изменение любого из них каким-либо образом изменит значение serialVersionUID. Поэтому вам не нужно возиться с ними, только если вы уверены, что не более чем одна версия класса будет когда-либо сериализована (либо через процессы, либо из хранилища позднее).

Если вы проигнорируете их пока, и найдите позже, что вам нужно каким-то образом изменить класс, но поддерживать совместимость с старой версией класса, вы можете использовать инструмент JDK serialver для генерации serialVersionUID в старом классе и явно установить это в новом классе. (В зависимости от ваших изменений вам может потребоваться также реализовать пользовательскую сериализацию, добавив методы writeObject и readObject - см. Serializable javadoc или вышеупомянутую главу 11.)

  • 33
    Таким образом, можно было бы беспокоиться о SerializableVersionUID, если бы вы были обеспокоены совместимостью со старыми версиями класса?
  • 10
    Да, в случае, если более новая версия изменит какой-либо открытый член на защищенный, SerializableVersionUID по умолчанию будет другим и вызовет InvalidClassExceptions.
Показать ещё 2 комментария
119

Вы можете сказать Eclipse игнорировать эти предупреждения serialVersionUID:

Окно > Предпочтения > Java > Компилятоp > Ошибки/предупреждения > Проблемы с потенциальным программированием

Если вы не знаете, есть много других предупреждений, которые вы можете включить в этом разделе (или даже некоторые из них сообщаются как ошибки), многие из них очень полезны:

  • Потенциальные проблемы программирования: возможное случайное логическое назначение
  • Потенциальные проблемы программирования: доступ к нулевому указателю.
  • Ненужный код: Локальная переменная никогда не читается
  • Ненужный код: избыточная нулевая проверка
  • Ненужный код: Ненужный листинг или "экземпляр"

и многие другие.

  • 20
    upvote, но только потому, что оригинальный постер ничего не сериализует. Если бы на плакате говорилось: «Я сериализирую эту штуку и ...», тогда вы получите вместо этого голос: P
  • 3
    Правда - я предполагал, что спрашивающий просто не хотел, чтобы его предупреждали
Показать ещё 2 комментария
105

serialVersionUID облегчает управление версиями сериализованных данных. Его значение хранится с данными при сериализации. При де-сериализации одна и та же версия проверяется, как сериализованные данные соответствуют текущему коду.

Если вы хотите изменить свои данные, вы обычно начинаете с serialVersionUID из 0 и ударяете его с каждым структурным изменением в ваш класс, который изменяет сериализованные данные (добавление или удаление непереходных полей).

Встроенный механизм де-сериализации (in.defaultReadObject()) откажется от де-сериализации из старых версий данных. Но если вы хотите, вы можете определить свою собственную readObject() -функцию, которая может считывать старые данные. Этот пользовательский код может затем проверить serialVersionUID, чтобы узнать, в какую версию находятся данные, и решить, как его десериализуйте. Этот метод управления версиями полезен, если вы храните сериализованные данные, которые сохраняются в нескольких версиях вашего кода.

Но хранение сериализованных данных за такой длительный промежуток времени не очень распространено. Гораздо чаще используется механизм сериализации для временной записи данных, например, кэш-памяти или отправки по сети другой программе с той же версией соответствующих частей кодовой базы.

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

Если вы забыли обновить поле, вы можете получить две разные версии класса с разной структурой, но с тем же serialVersionUID. Если это произойдет, механизм по умолчанию (in.defaultReadObject()) не обнаружит разницы и попытается дезацинировать несовместимые данные. Теперь у вас может возникнуть критическая ошибка времени выполнения или молчащий сбой (пустые поля). Эти типы ошибок могут быть трудно найти.

Итак, чтобы помочь в этом использовании, платформа Java предлагает вам выбор, не устанавливающий serialVersionUID вручную. Вместо этого хэш структуры класса будет генерироваться во время компиляции и использоваться как id. Этот механизм гарантирует, что у вас никогда не будет разных структур классов с одним и тем же идентификатором, и поэтому вы не получите эти неудачные попытки сериализации во время выполнения, упомянутые выше.

Но есть обратная сторона для автоматической генерации стратегии id. А именно, что генерируемые идентификаторы для одного и того же класса могут отличаться между компиляторами (как упоминал выше, Джон Скит). Поэтому, если вы передаете сериализованные данные между кодом, скомпилированным с разными компиляторами, рекомендуется поддерживать идентификаторы вручную в любом случае.

И если вы обратно совместимы с вашими данными, как в упомянутом первом случае, вы также, вероятно, захотите сохранить идентификатор самостоятельно. Это для того, чтобы получить читаемые идентификаторы и иметь больший контроль над тем, когда и как они меняются.

  • 3
    Добавление или удаление непереходных полей не делает сериализацию несовместимой с классом. Таким образом, нет никаких причин «наталкиваться» на такие изменения.
  • 4
    @EJP: А? Добавление данных определенно меняет сериализацию данных в моем мире.
Показать ещё 3 комментария
61

Что такое serialVersionUID и почему я должен его использовать?

SerialVersionUID является уникальным идентификатором для каждого класса, JVM использует его для сравнения версий класса, обеспечивающих загрузку одного и того же класса во время сериализации во время десериализации.

Указание одного дает больше контроля, хотя JVM создает его, если вы не укажете. Созданное значение может различаться между разными компиляторами. Кроме того, иногда вы просто хотите по какой-то причине запретить десериализацию старых сериализованных объектов [ backward incompatibility ], и в этом случае вам просто нужно изменить serialVersionUID.

Javadocs для Serializable говорят:

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

Поэтому вы должны объявить serialVersionUID, потому что это дает нам больше контроля.

В этой статье есть несколько хороших точек на эту тему.

  • 3
    @Vinothbabu, но serialVersionUID является статическим, поэтому статические переменные нельзя сериализовать. тогда почему jvm проверит версию, не зная, какая версия объекта десериализации
  • 3
    Единственное, что не упомянуто в этом ответе, - это то, что вы можете вызвать непредвиденные последствия, слепо включив serialVersionUID не зная, почему. Комментарий Тома Андерсона к ответу MetroidFan2002 касается этого: «Я бы сказал, что если вы не используете сериализацию для постоянного хранения, вам следует использовать @SuppressWarnings вместо добавления значения. Это меньше загромождает класс и сохраняет способность Механизм serialVersionUID защитит вас от несовместимых изменений. "
Показать ещё 1 комментарий
43

В первоначальном вопросе были заданы вопросы "почему это важно" и "пример", в которых этот Serial Version ID будет полезен. Ну, я нашел один.

Скажем, вы создаете класс Car, создаете его экземпляр и записываете его в поток объектов. Расплющенный объект автомобиля некоторое время находится в файловой системе. Между тем, если класс Car изменяется, добавляя новое поле. Позже, когда вы попытаетесь прочитать (т.е. десериализовать) уплощенный объект Car, вы получите java.io.InvalidClassException поскольку всем сериализуемым классам автоматически присваивается уникальный идентификатор. Это исключение выдается, когда идентификатор класса не равен идентификатору сплющенного объекта. Если вы действительно думаете об этом, исключение выдается из-за добавления нового поля. Вы можете избежать появления этого исключения, управляя версионированием самостоятельно, объявив явный serialVersionUID. Также есть небольшое преимущество в производительности, если явно объявить ваш serialVersionUID (потому что вычислять не нужно). Поэтому рекомендуется добавлять свой собственный serialVersionUID в свои классы Serializable, как только вы создадите их, как показано ниже:

public class Car {
    static final long serialVersionUID = 1L; //assign a long value
}
  • 0
    И каждому UID следует назначать случайное длинное число, а не 1L.
  • 1
    @abbas «Нужно» сделать это, почему? Пожалуйста, объясните, какая разница.
Показать ещё 4 комментария
33

Если вам никогда не понадобится сериализовать ваши объекты в массив байтов и отправить/сохранить их, вам не нужно беспокоиться об этом. Если да, то вы должны учитывать свой serialVersionUID, поскольку десериализатор объекта будет соответствовать его версии объекта, который имеет его загрузчик классов. Подробнее об этом читайте в спецификации языка Java.

  • 7
    Если вы не собираетесь сериализовать объекты, почему они сериализуются?
  • 6
    @erickson - родительский класс может быть сериализуемым, скажем, ArrayList, но вы хотите, чтобы ваш собственный объект (скажем, список измененных массивов) использовал его в качестве основы, но никогда не будет сериализовать созданную вами коллекцию.
Показать ещё 2 комментария
32

Если вы получите это предупреждение в классе, о котором вы никогда не думали о сериализации, и что вы не объявляли себя implements Serializable, это часто происходит потому, что вы унаследовали от суперкласса, который реализует Serializable. Часто тогда было бы лучше делегировать такой объект вместо использования наследования.

Итак, вместо

public class MyExample extends ArrayList<String> {

    public MyExample() {
        super();
    }
    ...
}

делать

public class MyExample {
    private List<String> myList;

    public MyExample() {
         this.myList = new ArrayList<String>();
    }
    ...
}

и в соответствующих методах вызовите myList.foo() вместо this.foo() (или super.foo()). (Это не подходит во всех случаях, но все же довольно часто.)

Я часто вижу, как люди расширяют JFrame или такие, когда им действительно нужно только делегировать это. (Это также помогает для автозаполнения в среде IDE, поскольку JFrame имеет сотни методов, которые вам не нужны, когда вы хотите вызвать свои собственные в своем классе.)

Один случай, когда предупреждение (или serialVersionUID) неизбежно, - это когда вы расширяетесь от AbstractAction, обычно в анонимном классе, добавляя только метод actionPerformed. Я думаю, что в этом случае не должно быть предупреждения (так как вы, как правило, не можете достоверно сериализовать и десериализовать такие анонимные классы в любом случае в разных версиях вашего класса), но я не уверен, как компилятор мог это распознать.

  • 4
    Я думаю, что вы правы в том, что композиция, а не наследование, имеет больше смысла, особенно когда вы обсуждаете такие классы, как ArrayList. Тем не менее, многие фреймворки требуют, чтобы люди выходили из абстрактных суперклассов, которые можно сериализовать (например, класс ActionForm в Struts 1.2 или Saxon ExtensionFunctionDefinition), и в этом случае это решение невозможно. Я думаю, что вы правы, было бы неплохо, если бы предупреждение было проигнорировано в определенных случаях (например, если вы выходили из абстрактного сериализуемого класса)
  • 2
    Конечно, если вы добавляете класс в качестве члена, а не наследуете его, вам придется написать метод-обертку для КАЖДОГО метода класса-члена, который вы хотите использовать, что сделает его невозможным в большом количестве ситуаций. Если в java нет функции, аналогичной __AUTOLOAD в perl, о которой я не знаю.
Показать ещё 5 комментариев
21

Чтобы понять значение поля serialVersionUID, нужно понимать, как работает сериализация/десериализация.

Когда объект класса Serializable сериализуется, Java Runtime связывает серийный номер версии (называемый serialVersionUID) с этим сериализованным объектом. В то время, когда вы десериализируете этот сериализованный объект, Java Runtime соответствует serialVersionUID сериализованного объекта с serialVersionUID этого класса. Если оба они равны, то только он переходит к дальнейшему процессу десериализации иначе бросает InvalidClassException.

Итак, мы заключаем, что для успешного завершения процесса сериализации/десериализации сериализованный объект serialVersionUID должен быть эквивалентен serialVersionUID этого класса. В случае, если программист явно указывает значение serialVersionUID в программе, то такое же значение будет связано с сериализованным объектом и классом, независимо от платформы сериализации и десериализации (например, сериализация может выполняться на платформе, например, с помощью окна или MS JVM и десериализация могут быть на другой платформе Linux с использованием Zing JVM).

Но в случае, если serialVersionUID не указан программистом, то, выполняя сериализацию \DeSerialization любого объекта, среда выполнения Java использует свой собственный алгоритм для ее вычисления. Этот алгоритм вычисления serialVersionUID варьируется от одной JRE к другой. Также возможно, что среда, в которой объект сериализуется, использует одну JRE (например: SUN JVM) и среду, в которой происходит десериализация, используется Linux Jvm (zing). В таких случаях serialVersionUID, связанный с сериализованным объектом, будет отличаться от serialVersionUID класса, рассчитанного в среде десериализации. В свою очередь десериализация не будет успешной. Поэтому, чтобы избежать таких ситуаций/проблем, программист должен всегда указывать serialVersionUID класса Serializable.

  • 2
    Алгоритм не меняется, но он немного занижен.
18

Что касается примера, когда недостающий serialVersionUID может вызвать проблему:

Я работаю над этим приложением Java EE, которое состоит из веб-модуля, который использует модуль EJB. Веб-модуль вызывает модуль EJB удаленно и передает POJO, который реализует Serializable в качестве аргумента.

Этот класс POJO's был упакован внутри банка EJB и внутри собственной банки в WEB-INF/lib веб-модуля. Они на самом деле одни и те же, но когда я упаковываю модуль EJB, я распаковываю этот баннер POJO, чтобы упаковать его вместе с модулем EJB.

Призыв к EJB был сбой при исключении ниже, потому что я не объявил его serialVersionUID:

Caused by: java.io.IOException: Mismatched serialization UIDs : Source
 (Rep.
 IDRMI:com.hordine.pedra.softbudget.domain.Budget:5CF7CE11E6810A36:04A3FEBED5DA4588)
 = 04A3FEBED5DA4588 whereas Target (Rep. ID RMI:com.hordine.pedra.softbudget.domain.Budget:7AF5ED7A7CFDFF31:6227F23FA74A9A52)
 = 6227F23FA74A9A52
18

Не беспокойтесь, расчет по умолчанию действительно хорош и достаточен для 99,9999% случаев. И если у вас возникнут проблемы, вы можете - как уже было сказано, ввести UID по мере необходимости (что маловероятно)

  • 0
    Мусор. Это достаточно для случая, когда класс не изменился. У вас нет никаких доказательств в поддержку «99,9999%».
16

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

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

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

ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
15

Обычно я использую serialVersionUID в одном контексте: когда я знаю, что он покинет контекст Java VM.

Я бы это знал, когда я использовал ObjectInputStream и ObjectOutputStream для своего приложения, или если я знаю, что используемая библиотека/фрейм будет использовать ее. SerialVersionID гарантирует, что разные виртуальные машины Java разных версий или поставщики будут взаимодействовать правильно или если они будут храниться и извлекаться вне VM, например HttpSession, данные сеанса могут оставаться даже во время перезагрузки и обновления сервера приложений.

Для всех остальных случаев я использую

@SuppressWarnings("serial")

так как большую часть времени по умолчанию serialVersionUID достаточно. Это включает Exception, HttpServlet.

  • 0
    Он не включает HttpServlet в контейнеры, где их можно заменить, или исключение в RMI, например.
14

Сначала мне нужно объяснить сериализацию.
Сериализация позволяет конвертировать объект в поток, для отправки этого объекта по сети ИЛИ Сохранить в файл ИЛИ сохранить в БД для использования в письмах.

Существуют правила для сериализации.

  • Объект сериализуется, только если его класс или его суперкласс реализует интерфейс Serializable

  • Объект сериализуем (сам реализует интерфейс Serializable), даже если его суперкласс не является. Однако первый суперкласс в иерархии сериализуемого класса, который не реализует интерфейс Serializable, ДОЛЖЕН иметь конструктор no-arg. Если это нарушено, readObject() создаст исключение java.io.InvalidClassException во время выполнения

  • Все примитивные типы сериализуемы.

  • Временные поля (с переходным модификатором) НЕ сериализуются (т.е. не сохраняются или не восстанавливаются). Класс, который реализует Serializable, должен отмечать переходные поля классов, которые не поддерживают сериализацию (например, поток файлов).

  • Статические поля (со статическим модификатором) не сериализованы.

Когда объект сериализован JAVA Runtime Сопоставляет номер серийной версии, который называется serialVersionID.

Где нам нужен serialVersionID: Во время десериализации, чтобы убедиться, что отправитель и получатель совместимы с сериализацией. Если приемник загрузил класс с помощью другого serialVersionID, то десериализация закончится с InvalidClassCastException.
 Сериализуемый класс может объявить свой собственный serialVersionUID явно, объявив поле с именем "serialVersionUID", которое должно быть статическим, окончательным и длинным:

Попробуйте это с примером.

import java.io.Serializable;    
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private String empname;
private byte empage;

public String getEmpName() {
    return name;
}
public void setEmpName(String empname) {
    this.empname = empname;
}
public byte getEmpAge() {
    return empage;
}
public void setEmpAge(byte empage) {
    this.empage = empage;
}

public String whoIsThis() {
    StringBuffer employee = new StringBuffer();
    employee.append(getEmpName()).append(" is ).append(getEmpAge()).append("
years old  "));
    return employee.toString();
}
}

Создать объект Serialize

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Writer {
public static void main(String[] args) throws IOException {
    Employee employee = new Employee();
    employee.setEmpName("Jagdish");
    employee.setEmpAge((byte) 30);

    FileOutputStream fout = new 
FileOutputStream("/users/Jagdish.vala/employee.obj");
    ObjectOutputStream oos = new ObjectOutputStream(fout);
    oos.writeObject(employee);
    oos.close();
    System.out.println("Process complete");
}
}

Deserializ объект

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class Reader {
public static void main(String[] args) throws ClassNotFoundException, 
IOException {
    Employee employee = new Employee();
    FileInputStream fin = new 
    FileInputStream("/users/Jagdish.vala/employee.obj");
    ObjectInputStream ois = new ObjectInputStream(fin);
    employee = (Employee) ois.readObject();
    ois.close();
    System.out.println(employee.whoIsThis());
 }
}    

ПРИМЕЧАНИЕ. Теперь измените serialVersionUID класса Employee и сохраните:

private static final long serialVersionUID = **4L**;

И выполните класс Reader. Не выполнять класс Writer, и вы получите исключение.

Exception in thread "main" java.io.InvalidClassException: 
com.jagdish.vala.java.serialVersion.Employee; local class incompatible: 
stream classdesc serialVersionUID = 1, local class serialVersionUID = 4
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at com.krishantha.sample.java.serialVersion.Reader.main(Reader.java:14)
14

Данные поля представляют некоторую информацию, хранящуюся в классе. Класс реализует интерфейс Serializable, поэтому eclipse автоматически предложит объявить поле serialVersionUID. Давайте начнем со значения 1, установленного там.

Если вы не хотите, чтобы это предупреждение появилось, используйте это:

@SuppressWarnings("serial")
11

SerialVersionUID используется для управления версиями объекта. вы также можете указать serialVersionUID в своем файле класса. Следствием не указания serialVersionUID является то, что когда вы добавляете или изменяете любое поле в классе, тогда уже сериализованный класс не сможет восстановить, потому что serialVersionUID, сгенерированный для нового класса и для старого сериализованного объекта, будет другим. Процесс сериализации Java основан на правильном serialVersionUID для восстановления состояния сериализованного объекта и бросает java.io.InvalidClassException в случае несоответствия serialVersionUID

Подробнее: http://javarevisited.blogspot.com/2011/04/top-10-java-serialization-interview.html#ixzz3VQxnpOPZ

10

Было бы неплохо, если CheckStyle мог убедиться, что serialVersionUID в классе, который реализует Serializable, имеет хорошее значение, то есть он соответствует тому, что будет генерировать генератор идентификаторов серийной версии. Если у вас есть проект с большим количеством сериализуемых DTO, например, помня, что нужно удалить существующий serialVersionUID и восстановить его, это боль, и в настоящее время единственный способ (который я знаю) проверить это - это регенерировать для каждого класса и сравнить с Старый. Это очень больно.

  • 8
    Если вы устанавливаете serialVersionUID всегда на одно и то же значение, которое генерирует генератор, вам это вообще не нужно. В конце концов, смысл его существования - оставаться неизменным после изменений, когда класс все еще совместим.
  • 4
    Причина в том, что разные компиляторы имеют одинаковое значение для одного и того же класса. Как объяснено в javadocs (также ответили выше), сгенерированная версия является хрупкой и может варьироваться, даже если класс должным образом десериализуем. Если вы каждый раз запускаете этот тест на одном и том же компиляторе, он должен быть безопасным. Бог поможет вам, если вы обновите JDK и появится новое правило, даже если ваш код не изменился.
Показать ещё 3 комментария
8

Зачем использовать SerialVersionUID внутри класса Serializable в Java?

Во время serialization среда выполнения Java создает номер версии для класса, чтобы впоследствии ее можно было де-сериализовать. Этот номер версии известен как SerialVersionUID в Java.

SerialVersionUID используется для создания сериализованных данных. Вы можете только де-сериализовать класс, если он SerialVersionUID соответствует сериализованному экземпляру. Когда мы не объявляем SerialVersionUID в нашем классе, среда выполнения Java генерирует его для нас, но не рекомендуется. Он рекомендовал объявить SerialVersionUID как переменную private static final long, чтобы избежать механизма по умолчанию.

Когда вы объявляете класс как Serializable, реализуя интерфейс-маркер java.io.Serializable, Java runtime сохраняет экземпляр этого класса на диск с использованием механизма Serialization по умолчанию, если вы не настроили процесс с использованием интерфейса Externalizable.

см. также Зачем использовать SerialVersionUID внутри класса Serializable в Java

Код: javassist.SerialVersionUID

6

Если вы хотите изменить огромное количество классов, в которых не было установленного параметра serialVersionUID, в то же время поддерживая совместимость со старыми классами, такие инструменты, как IntelliJ Idea, Eclipse, сокращаются по мере генерации случайных чисел и не работают на кучу файлов за один раз. Я прихожу к следующему bash script (я прошу прощения за пользователей Windows, подумайте о покупке Mac или конвертируйте в Linux), чтобы облегчить исправление проблемы serialVersionUID:

base_dir=$(pwd)                                                                  
src_dir=$base_dir/src/main/java                                                  
ic_api_cp=$base_dir/target/classes                                               

while read f                                                                     
do                                                                               
    clazz=${f//\//.}                                                             
    clazz=${clazz/%.java/}                                                       
    seruidstr=$(serialver -classpath $ic_api_cp $clazz | cut -d ':' -f 2 | sed -e 's/^\s\+//')
    perl -ni.bak -e "print $_; printf qq{%s\n}, q{    private $seruidstr} if /public class/" $src_dir/$f
done

вы сохраните этот script, скажем, add_serialVersionUID.sh для вас ~/bin. Затем запустите его в корневом каталоге вашего проекта Maven или Gradle, например:

add_serialVersionUID.sh < myJavaToAmend.lst

Этот .lst включает в себя список java файлов для добавления serialVersionUID в следующем формате:

com/abc/ic/api/model/domain/item/BizOrderTransDO.java
com/abc/ic/api/model/domain/item/CardPassFeature.java
com/abc/ic/api/model/domain/item/CategoryFeature.java
com/abc/ic/api/model/domain/item/GoodsFeature.java
com/abc/ic/api/model/domain/item/ItemFeature.java
com/abc/ic/api/model/domain/item/ItemPicUrls.java
com/abc/ic/api/model/domain/item/ItemSkuDO.java
com/abc/ic/api/model/domain/serve/ServeCategoryFeature.java
com/abc/ic/api/model/domain/serve/ServeFeature.java
com/abc/ic/api/model/param/depot/DepotItemDTO.java
com/abc/ic/api/model/param/depot/DepotItemQueryDTO.java
com/abc/ic/api/model/param/depot/InDepotDTO.java
com/abc/ic/api/model/param/depot/OutDepotDTO.java

Этот script использует инструмент JDK serialVer под капотом. Поэтому убедитесь, что ваш $JAVA_HOME/bin находится в PATH.

  • 1
    Дает мне идею: всегда регенерируйте uid серийной версии с помощью инструмента, подобного этому, никогда не делайте вручную, прежде чем делать релиз - таким образом, вы не забудете внести изменения в класс, чей uid серийной версии должен был измениться из-за фактической несовместимое изменение. Отслеживать это вручную очень сложно.
5

Каждый раз, когда объект сериализуется, объект имеет штамп с номером идентификатора версии для класса объекта. Этот идентификатор называется serialVersionUID и вычисляется на основе информации о структура классов. Предположим, что вы создали класс Employee, и у него есть версия id 333 (назначенная JVM). Теперь, когда вы будете сериализовывать объект этого класса (предположим, объект Employee), JVM присваивает ему UID как # 333.

Рассмотрим ситуацию - в будущем вам нужно отредактировать или изменить свой класс, и в этом случае, когда вы измените его, JVM назначит ему новый UID (предположим # 444). Теперь, когда вы пытаетесь десериализовать объект employee, JVM будет сравнивать идентификатор версии объекта сериализации объекта (Employee object) (№333) с классом класса i.e # 444 (поскольку он был изменен). При сравнении JVM найдет и версию UID разных, и, следовательно, Deserialization не удастся. Следовательно, если serialVersionID для каждого класса определяется самим программистом. Он будет таким же, даже если класс будет развиваться в будущем, и поэтому JVM всегда найдет, что класс совместим с сериализованным объектом, даже если класс изменен. Для получения дополнительной информации вы можете обратиться к главе 14 главы HEAD FIRST JAVA.

  • 1
    Каждый раз, когда класс объекта сериализуется, передается serialVersionUID . Это не отправлено с каждым объектом.
5

Этот вопрос очень хорошо документирован в "Эффективной Java" Джошуа Блоха. Очень хорошая книга и должна читать. Я расскажу о некоторых из следующих причин:

Сериализация выполняется с номером Serial для каждого сериализуемого класса. Этот номер называется serialVersionUID. Теперь за этим числом есть математика, и она выходит на основе полей/методов, определенных в классе. Для того же класса одна и та же версия генерируется каждый раз. Этот номер используется во время десериализации, чтобы убедиться, что отправитель и получатель сериализованного объекта загружают классы для этого объекта, которые совместимы с сериализацией. Если получатель загрузил класс для объекта с другим идентификатором serialVersionUID, чем у соответствующего класса отправителя, то десериализация приведет к исключению InvalidClassException.

Если класс сериализуем, вы также можете объявить свой собственный serialVersionUID явно, объявив поле с именем "serialVersionUID", которое должно быть статическим, окончательным и длинным. Большинство IDE, таких как Eclipse, помогают генерировать эту длинную строку.

0

Простое объяснение
1. Сериализуете ли вы данные? Сериализация в основном записывает данные класса в файл/поток/и т.д. Десериализация читает эти данные обратно в класс.
2. Собираетесь ли вы пойти в производство? Если вы просто тестируете что-то с неважными/фальшивыми данными, не беспокойтесь об этом (если только вы не тестируете сериализацию напрямую).
3. Это первая версия? Если это так, установите serialVersionUID=1L.
4. Это вторая, третья и т.д. Версия? Теперь вам нужно побеспокоиться о serialVersionUID, и вы должны изучить его более подробно.

По сути, если вы не обновите версию правильно, когда вы обновляете класс, который вам нужен для записи/чтения, вы получите сообщение об ошибке при попытке прочитать старые данные.

-2

Всякий раз, когда вы проверяете код (этот конкретный класс) на системы управления версиями, вы можете просто изменить SerialVersionUID на метку версии, убедившись, что его длинный тип данных. Это также легко запомнить.

Пример: если метка версии - App11132017.0, вы можете ввести ее как 111320170.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню