Зачем генерировать длинный serialVersionUID вместо простого 1L?


210

Когда класс реализует Serializable в Eclipse, у меня есть два варианта: добавить по умолчанию serialVersionUID(1L)или сгенерированный serialVersionUID(3567653491060394677L). Я думаю, что первый круче, но много раз я видел людей, использующих второй вариант. Есть ли причина для генерации long serialVersionUID?


49
Как это точно дубликат? Я не спрашиваю, зачем вообще его генерировать, но зачем генерировать длинный serialVersionUID.
IAdapter

13
Когда Джон Скит использует serialVersionUID, он использует 0L: stackoverflow.com/questions/605828/… ;)
Ханно Фитц

8
@HannoFietz: Точное предложение таково: «Для простоты я бы предложил начать с 0 и увеличивать его на 1 каждый раз, когда вам нужно». Так что, похоже, он использует 0Lтолько изначально.
ИЛИ Mapper

7
@ORMapper: Вы намекаете, что Джону Скиту когда-нибудь придется вернуться и обновить написанный им код? Даже на грани структурной несовместимости. Gasp! Ересь!
Thilo

Ответы:


90

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

Посмотрите Спецификацию Сериализации Java для получения дополнительной информации.


69

Цель UID версии сериализации состоит в том, чтобы отслеживать различные версии класса, чтобы выполнить правильную сериализацию объектов.

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

Всегда использование одного и того же идентификатора, например, 1Lозначает, что в будущем, если определение класса будет изменено, что приведет к изменениям в структуре сериализованного объекта, будет высокая вероятность возникновения проблем при попытке десериализации объекта.

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

Вот несколько ссылок на статьи, которые обсуждают сериализацию и управление версиями классов:


50
Идея использования 1L заключается в том, что вы увеличиваете его каждый раз, когда изменяете свойства или методы класса.
Powerlord

39
Это не влияет на производительность среды выполнения, так как позволяет автоматически генерировать serialversionUID - он генерируется во время компиляции с помощью javac ... если вы декомпилируете байт-код класса, вы фактически статически увидите переменную в байт-коде.
Джаред

11
Еще одно замечание - управляя числом явно, вы решаете, когда вы считаете версии класса «совместимыми», вместо того, чтобы требовать, чтобы определение класса было точно таким же.
Скотт Стэнчфилд

23
@ Джаред В соответствии с пунктом 75 «Эффективного Java» Джоша Блоха: 2-е издание: «объявляйте явный UID последовательной версии в каждом сериализуемом классе, который вы пишете .... Если не указан UID последовательной версии, для его генерации во время выполнения требуются дорогостоящие вычисления. «.
Колин К

12
@coobird Это, по-видимому, основная причина, по которой не рекомендуется использовать serialVersionUID по умолчанию. Note - It is strongly recommended that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected serialVersionUID conflicts during deserialization, causing deserialization to fail. Приведенный выше комментарий был взят из спецификации сериализации объектов Java версии 6.0
user624558

20

Основная причина для сгенерированного - сделать его совместимым с существующей версией класса, в которой уже есть постоянные копии.


1
ОК, но то же самое будет, если у меня всегда есть 1л. Все будет совместимо, даже если я сделаю какие-либо изменения.
grep

@grep попробуйте переименовать поле и посмотреть, что получится.
Trejkaz

1
@grep, суть в том, что если у вас есть класс, в котором ранее отсутствовал serialVersionUID, он автоматически получил бы сгенерированный класс. Итак, теперь вы хотите начать устанавливать его явно, если установить 1L, это сделает его несовместимым с существующим классом, а использование сгенерированного значения сделает его совместимым.
marc82ch

15

«Длинное» значение по умолчанию serialVersionUID- это значение по умолчанию, как определено Спецификацией Сериализации Java , вычисленное из поведения сериализации по умолчанию.

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

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


11

Вы обязательно должны создавать serialVersionUID каждый раз, когда определяете реализующий класс java.io.Serializable. Если вы этого не сделаете, он будет создан для вас автоматически, но это плохо. Автоматически генерируемый serialVersionUID основан на сигнатурах методов вашего класса, поэтому, если вы в будущем измените свой класс для добавления метода (например), десериализация «старых» версий класса завершится неудачно. Вот что может произойти:

  1. Создайте первую версию вашего класса, не определяя serialVersionUID.
  2. Сериализация экземпляра вашего класса в постоянное хранилище; serialVersionUID автоматически генерируется для вас.
  3. Измените свой класс, чтобы добавить новый метод, и повторно разверните ваше приложение.
  4. Попытайтесь десериализовать экземпляр, который был сериализован на шаге 2, но теперь он терпит неудачу (когда это должно произойти), потому что у него есть другой автоматически сгенерированный serialVersionUID.

1
На самом деле, десериализация старых версий класса действительно должна потерпеть неудачу, потому что они больше не совпадают. Вы предлагаете генерировать serialVersionUID самостоятельно, чтобы предотвратить (де) сбои сериализации при изменении сигнатуры класса. Хотя ваше предложение уместно, ваше объяснение его цели просто неверно и вводит в заблуждение. Было бы разумно изменить ваш ответ.
Mostruash

6

Если вы не укажете serialVersionUID, то Java сделает его на лету. Сгенерированный serialVersionUID - это число. Если вы изменяете что-то в своем классе, что на самом деле не делает ваш класс несовместимым с предыдущими сериализованными версиями, но меняет хеш, то вам нужно использовать сгенерированный serialVersionUID с очень большим числом (или «ожидаемое» число из сообщения об ошибке) , В противном случае, если вы все отслеживаете сами, 0, 1, 2 ... лучше.


Вы имели в виду ==> 1. Если вы хотите, чтобы различные изменения классов были совместимыми, используйте сгенерированный. 2. Если вы хотите, чтобы разные версии классов были несовместимыми, используйте версию по умолчанию и будьте осторожны в приращении. Я правильно понял?
JavaDeveloper

4

Когда вы используете serialVersionUID (1L) вместо генерации serialVersionUID (3567653491060394677L), вы что-то говорите.

Вы говорите, что на 100% уверены, что ни одна система, которая когда-либо будет касаться этого класса, не имеет несовместимой сериализованной версии этого класса с номером версии 1.

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

Вы можете мучиться из-за этого. Или вы можете сыграть в лотерею в надежде проиграть. Если вы генерируете версию, у вас есть небольшой шанс, что что-то пойдет не так. Если вы предполагаете: «Эй, я уверен, никто еще не использовал 1», ваши шансы больше, чем крошечные. Именно потому, что мы все думаем, что 0 и 1 - это круто, у вас больше шансов попасть в них.

-

Когда вы генерируете serialVersionUID (3567653491060394677L), а не используете serialVersionUID (1L), вы что-то говорите.

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

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

После всего этого разговора об игре в лотерею у меня могло сложиться впечатление, что serialVersionUID генерируется случайным образом. Фактически, пока диапазон чисел равномерно распределен по каждому возможному значению Long, это было бы хорошо. Однако на самом деле это делается так:

http://docs.oracle.com/javase/6/docs/platform/serialization/spec/class.html#4100

Единственное отличие, которое вы получаете, заключается в том, что вам не нужен источник случайных данных Вы используете изменения в самом классе, чтобы изменить результат. Но по принципу «голубиных отверстий» все еще есть вероятность, что он может пойти не так и столкнуться. Это невероятно невероятно. Так что удачи в извлечении пива из меня.

Однако, даже если класс будет когда-либо жить только в одной системе и одной кодовой базе, думая, что увеличение числа вручную дает вам нулевую вероятность столкновений, просто означает, что вы не понимаете людей. :)


Если «система» касается класса, то есть изменяет класс таким образом, что сериализация становится несовместимой, тогда возникает вопрос, изменит ли эта система также serialVersionUID. Я не думаю, что шансы меньше, что он не забудет изменить его, когда он будет длинным. Я думаю, что скорее наоборот, если число легче запомнить, изменения выше, и я замечаю, что случайно не изменил его.
Рето Гмюр

2
Это неверно! Когда вы генерируете serialVersionUID и объявляете это значение в своем исходном коде, а не 1L, или ничего, что вы на самом деле говорите: я хочу, чтобы в будущем возможно было необнаруженное столкновение с неопределенными эффектами, и я не хочу, чтобы java или кто-либо другой предотвращал это. , Ява параноидальна, но послушна. Люди обычно не связываются с большими числами. Таким образом, когда класс меняется, java может десериализовать старые несовместимые версии. MwoaHaHa ...;)
Superole

1

Ну, serialVersionUID является исключением из правила, что «статические поля не сериализуются». ObjectOutputStream каждый раз записывает значение serialVersionUID в выходной поток. ObjectInputStream считывает его обратно, и если значение, считанное из потока, не соответствует значению serialVersionUID в текущей версии класса, то оно генерирует исключение InvalidClassException. Более того, если нет serialVersionUID, официально объявленного в сериализуемом классе, компилятор автоматически добавляет его со значением, созданным на основе полей, объявленных в классе.


0

Потому что во многих случаях идентификатор по умолчанию не является уникальным. поэтому мы создаем идентификатор для создания уникальной концепции.


Можете ли вы отредактировать свой ответ, чтобы конкретизировать его? Это похоже на комментарий здесь. Спасибо.
Серый

0

Чтобы добавить в ответ @David Schmitts, я бы всегда использовал стандартное значение 1L вне соглашения. Я только должен был вернуться и изменить некоторые из них несколько раз, но я знал это, когда вносил изменения и обновлял номер по умолчанию по одному каждый раз.

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


0

Цель UID версии сериализации состоит в том, чтобы отслеживать различные версии класса, чтобы выполнить правильную сериализацию объектов.

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

Простое объяснение:

Вы сериализуете данные?

Сериализация - это запись данных класса в файл / поток / и т. Д. Десериализация читает эти данные обратно в класс.

Собираетесь ли вы пойти в производство?

Если вы просто тестируете что-то с неважными / поддельными данными, не беспокойтесь об этом (если только вы не тестируете сериализацию напрямую).

Это первая версия?

Если это так, установите serialVersionUID = 1L.

Это вторая, третья и т. Д. Версия?

Теперь вам нужно побеспокоиться о serialVersionUID, и вам стоит углубиться в это.

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.