Что такое serialVersionUID и почему я должен его использовать? - PullRequest
2759 голосов
/ 13 ноября 2008

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

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

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

Ответы [ 24 ]

2106 голосов
/ 13 ноября 2008

Документы для 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 использовался частный модификатор, где это возможно, поскольку такие объявления применяются только к немедленно объявленным полям класса serialVersionUID, которые не используются как унаследованные члены.

441 голосов
/ 13 ноября 2008

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

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

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

282 голосов
/ 13 ноября 2008

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

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

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

128 голосов
/ 13 ноября 2008

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

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

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

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

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

102 голосов
/ 03 октября 2012

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

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

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

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

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

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

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

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

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

67 голосов
/ 17 октября 2013

Что такое serialVersionUID и зачем его использовать?

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

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

Javadocs для Serializable скажем :

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

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

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

51 голосов
/ 07 апреля 2013

В первоначальном вопросе спрашивалось «почему это важно» и «пример», где это 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
}
34 голосов
/ 13 ноября 2008

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

33 голосов
/ 03 февраля 2011

Если вы получаете это предупреждение для класса, о котором вы никогда не задумываетесь о сериализации, и что вы не объявили себя 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-метод. Я думаю, что в этом случае не должно быть предупреждения (так как обычно вы не можете надежно сериализовать и десериализовать такие анонимные классы в любом случае в разных версиях вашего класса), но я не уверен, как компилятор мог это распознать.

22 голосов
/ 02 июня 2013

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

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

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

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

...