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

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

10 ответов

Решение

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Когда вы используете 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Это вторая, третья и т.д. прод версия?

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

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

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

Другие вопросы по тегам