Что означает 1L serialVersionUID? Когда я могу использовать это значение по умолчанию 1L?

17

Существует 3 способа определения serialVersionUID:

1. private static final long serialVersionUID = 1L; (Default)
2. private static final long serialVersionUID = -8940196742313994740L; (Generated)
3. Don't define serialVersionUID and let the JVM define it at runtime. @Lance Java

Но я не понимаю первого пути!

Я уже видел это, что кто-то определяет «serialVersionUID = 1L» для всех java-классов в исходном коде.

В чем смысл? Это полезно?

Если все классы имеют один и тот же serialVersionUID 1L, нет ли проблем?

    
задан Ying Style 18.02.2014 в 11:29
источник
  • Фактическое значение не имеет значения, важно то, что вы его изменяете при внесении несовместимых изменений в класс. Посмотрите здесь: stackoverflow.com/questions/3284979/... –  qqilihq 18.02.2014 в 11:30
  • Обычно не имеет значения, к чему назначен serialVersionUID. –  Rhys 18.02.2014 в 11:33
  • @Rhys Это утверждение слишком общее. –  qqilihq 18.02.2014 в 11:36
  • Большой вопрос. Любой другой язык использует такую ​​функцию? –  Leo 18.02.2014 в 11:39
  • Примечание. Существует также третий вариант. Не определяйте serialVersionUID и пусть JVM определяет его во время выполнения. –  lance-java 18.02.2014 в 11:46

8 ответов

12
  

В чем смысл? Это полезно?

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

  

Если все классы имеют один и тот же serialVersionUID 1L, нет ли проблем?

Нет - serialVersionUID - для каждого класса .

    
ответ дан Michael Borgwardt 18.02.2014 в 11:36
источник
8

Это объясняет здесь :

  

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

Из javadoc :

  

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

Полезные ссылки

  1. java.io.Serializable
  2. Почему я должен беспокоиться о serialVersionUID? (StackOverflow)
  3. serialVersionUID в Java Serialization
ответ дан Aarav 18.02.2014 в 11:34
источник
  • Источник этой очевидной цитаты? –  EJP 18.02.2014 в 11:36
6

JVM будет вызывать InvalidClassException , если serialVersionUID сериализованного объекта не соответствует serialVersionUID класса, который десериализуется как.

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

Есть случаи, когда вы не хотите, чтобы serialVersionUID изменилось. Например, вы можете принять старые версии объекта в своем приложении. В этом случае вы можете оставить serialVersionUID теми же и новыми полями, которые пройдут как null.

    
ответ дан lance-java 18.02.2014 в 11:35
источник
6

Да, я видел код, который также определил serialVersionUID . Я думаю, что это плохая идея.

В этом контексте есть только одно «отличное» значение для поля serialVersionUID ; то есть нуль ... 0L . «Нуль» означает «вычислить версию id во время выполнения , применяя стандартный алгоритм» на основе фактического класса, который вы сериализуете / десериализуете. Это означает, что всякий раз, когда меняется эффективная сигнатура сериализации вашего кода, код сериализации / десериализации будет использовать другой идентификатор версии. С точки зрения безопасности (большой картины), это самая безопасная вещь, хотя она также несколько неэффективна и по преимуществу более хрупкая.

  

В чем смысл?

1L не имеет особого значения. Это просто число, которое будет соответствовать 1L как «другая» версия id.

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

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

  • С другой стороны, вы используете сгенерированный идентификатор версии, вы (программист) можете принять собственное решение о том, когда нужно восстановить идентификатор. И когда вы решите регенерировать, идентификатор изменится, только если подпись класса имеет . (Если представление классов и т. Д. Не изменилось, регенерированная подпись должна быть идентичной исходной!) И когда изменяется id , вы можете подумать о том, добавлять ли пользовательские методы ('readObject', и т. д.) для устранения несовместимости.

  • Однако, если вы используете 1L , вы не можете определить, требуется ли изменить идентификатор версии , не проверяя историю вашего кода и сравнивая старые / новые версии классов ... назад, насколько вам нужно.

  

Это полезно?

Это зависит от того, что вы считаете «полезным». Если вы считаете, что это хорошо, чтобы установить идентификатор версии, чтобы «доверять мне, это нормально», то полезно использовать 1L .

Мое воспоминание о том, что некоторые версии Eclipse предлагают 1L как одну из возможных автокоррекций для предупреждения о недостатке serialVersionUID . Вероятно, откуда появились экземпляры.

    
ответ дан Stephen C 18.02.2014 в 11:43
источник
  • не является тем утверждением, что, указав номер, он аналогичен фиксации контроля версий? –  Thufir 18.08.2017 в 17:20
  • Чья это проблема? Не ОП. Не мой. И было бы нецелесообразно менять число на каждой фиксации. Если это то, что вы подразумеваете ..... –  Stephen C 19.08.2017 в 03:28
3

Представьте, что вы пишете класс с serialVersionUID , создаете экземпляр, затем сериализуете его в файл (с помощью ObjectOutputStream)

Затем вы изменяете класс.

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

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

    
ответ дан NickJ 18.02.2014 в 11:38
источник
  • Установка его в 1L не отключает проверку версий. –  EJP 18.02.2014 в 11:39
  • Если вы всегда устанавливаете его в 1L, это будет, потому что serialVersionUID всегда будет одинаковым, поэтому проверка всегда будет проходить. –  NickJ 18.02.2014 в 11:40
3

Значение используется при сериализации объекта и де-сериализации объекта обратно в JVM.

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

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

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

    
ответ дан sakura 18.02.2014 в 11:36
источник
  • Почему это лучшая практика? Где это так? –  EJP 18.02.2014 в 11:38
  • Согласитесь. Модифицированный ответ специально указанная вами часть. –  sakura 18.02.2014 в 11:44
2

Вы можете присвоить значение long serialVersionUID , но вы должны изменить его каждый раз, когда вы изменяете свой класс.

Второй выглядит как сгенерированный serialVersionUID , основанный на функциях текущей версии класса.

    
ответ дан Marco Bolis 18.02.2014 в 11:31
источник
  • Где говорится, что вы должны менять его каждый раз, когда вы изменяете свой класс! –  EJP 18.02.2014 в 11:37
  • Я не знаю, где он это говорит, но попытка десериализации объекта, сериализованного с помощью другой версии класса, может привести к неожиданному поведению. Назначение другого serialVersionUID приведет к возникновению исключения. Никаких неприятных сюрпризов ... –  Marco Bolis 18.02.2014 в 11:43
  • Не обязательно менять его каждый раз, но если вы этого не сделаете, вам нужно будет определить, совместима ли новая версия класса со старыми сериализованными версиями. Сделать такое определение не является тривиальной задачей. Безопаснее менять его каждый раз. –  NickJ 18.02.2014 в 14:18
2

Важно подчеркнуть тот факт, что при реализации класса интерфейс Serializable делает поля ALL не объявленными transient частью экспортированного API класса , объявлены ли эти поля private .

Другими словами, реализация Serializable:

  • прерывает инкапсуляцию. Если у класса есть шанс стать успешным, долгоживущим классом, тогда вы должны поддерживать сериализованную форму ... навсегда.

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

  • может создавать проблемы безопасности для вашего класса и его приложения. Deserialization представляет собой способ создания объектов Java без конструктора, поэтому можно нарушать инварианты класса, предоставляя потоки байтов-изгоев в средство десериализации.

serialVerionUID следует рассматривать как свойство сериализованной формы . Он предназначен для передачи одной JVM независимо от того, существует ли разница между сериализованной формой экземпляра класса, которую он получает, и сериализованной формой того же класса, который был сделан (возможно) где-то еще.

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

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

    
ответ дан scottb 25.01.2015 в 00:57
источник