Сериализация Java со статической инициализацией

StackOverflow https://stackoverflow.com/questions/41499

  •  09-06-2019
  •  | 
  •  

Вопрос

В Java статические и временные поля не сериализуются.Однако я обнаружил, что инициализация статических полей приводит к изменению сгенерированного SerialVersionUID.Например, static int MYINT = 3; приводит к изменению серийногоVersionUID.В этом примере это имеет смысл, поскольку разные версии класса будут получать разные начальные значения.Почему любая инициализация меняет серийныйVersionUID?Например, static String MYSTRING = System.getProperty("foo"); также приводит к изменению серийногоVersionUID.

Если быть точным, мой вопрос заключается в том, почему инициализация с помощью метода приводит к изменению серийногоVersionUID.Проблема, с которой я столкнулся, заключается в том, что я добавил новое статическое поле, которое было инициализировано значением системного свойства (getProperty).Это изменение вызвало исключение сериализации при удаленном вызове.

Это было полезно?

Решение

Некоторую информацию об этом вы можете найти в ошибка 4365406 и в алгоритм вычислений серийная версияUID.По сути, при изменении инициализации вашего static член с System.getProperty(), компилятор вводит новый static свойство в вашем классе, ссылающееся на System класс (я предполагаю, что System класс ранее не упоминался в вашем классе), и поскольку это свойство, введенное компилятором, не является закрытым, оно принимает участие в serialVersionUID расчет.

Мораль:всегда используйте явный serialVersionUID, вы сэкономите несколько циклов процессора и избавите от головной боли :)

Другие советы

Автоматический SerialVersionUID рассчитывается на основе членов класса.Их можно отобразить для файла класса с помощью инструмента javap в Sun JDK.

В случае, упомянутом в вопросе, добавляемый/удаляемый элемент является статическим инициализатором.В файлах классов это отображается как ()V.Содержимое метода можно дизассемблировать с помощью javap -c.Вы должны быть в состоянии распознать вызов System.getProperty("foo") и назначение MYSTRING.Однако присвоение строкового литерала (или любой константы времени компиляции, как определено в спецификации языка Java) поддерживается непосредственно файлом класса, что устраняет необходимость в статическом инициализаторе.

Распространенным случаем кода, ориентированного на J2SE 1.4 (используйте -source 1.4 -target 1.4) или более ранней версии, являются статические поля в старых экземплярах классов, которые отображаются как литералы класса в исходном коде (MyClass.class).Экземпляр класса ищется по требованию с помощью Class.forName и сохраняется в статическом поле.Именно это статическое поле нарушает SerialVersionUID.Начиная с J2SE 5.0, вариант кода операции ldc обеспечивает прямую поддержку литералов классов, устраняя необходимость в синтетическом поле.Опять же, все это можно показать с помощью javap -c.

Если я правильно прочитал спецификацию, автоматический serialVersionUID не должно меняться, если вы измените значение статического или переходного поля.Взгляни на Глава 5.6 Спец.

Однако, если вы немного подумаете об этом - вы начнете с сериализации объекта, который имеет static int MYINT = 3, когда вы затем десериализуете класс, вы ожидаете получить обратно тот же объект, то есть с помощью MYINT = 3.Итак, если вы измените статическую инициализацию, вы ожидаете, что serialVersionUID измениться, потому что вы не можете вернуть тот же объект снова.

В любом случае, сохраните это во всех своих сериализуемых классах, и вы сможете контролировать serialVersionUID:

private static final long serialVersionUID = 7526472295622776147L;

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top