Настройка механизма сериализации 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Настройка механизма сериализации



 

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

Руководствуясь подобными соображениями, создатели Java предусмотрели возможность включать в состав класса специальные private – версии методов writeObject() и readObject(). Методы активизируются потоками ObjectOutputStream и ObjectInputStream соответственно в тот момент, когда необходимо осуществить сериализацию и десериализацию объекта. Подобные переопределённые варианты методов writeObject() и readObject() вызываются только в контексте объектов соответствующих классов, и методы несут ответственность исключительно за состояние объекта класса как такового, в том числе и в части, унаследованной от базового класса, не поддерживающего механизм сериализации.

Если в составе класса реализованы собственные варианты методов writeObject() и readObject(), они не должны обращаться к одноимённым методам базового класса

private void writeObject(ObjectOutputStream out) throws IOException

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException

В объявлении метода writeObject() обозначена вероятность выбрасывания им исключения типа IOException, так как оно может быть сгенерировано любым из «низкоуровневых» методов write(), которые будут здесь вызываться, и если подобное произойдёт, процесс сериализации должен быть завершён аварийным образом. В предложении throws объявления метода readObject() также следует упомянуть исключение IOException – оно может быть выброшено вызываемыми методами read(), а в такой ситуации процесс десериализации следует остановить. Помимо того, метод readObject() способен генерировать и исключение типа ClassNotFoundException, так как в общем случае при десериализации полей текущего объекта может потребоваться загрузка и других классов.

Существует ограничение, которое следует принять во внимание, приступая к настройке механизма сериализации: нельзя присваивать значение полю final в теле метода readObject(), поскольку поля final могут быть проинициализированы только в блоках инициализации или конструкторах. Механизм сериализации, предусмотренный по умолчанию, однако, способен «обходить» это ограничение – он вполне нормально работает и с теми классами, в составе которых имеются поля final.

 

Контроль версий объектов

 

Реализация классов со временем изменяется. Если подобное происходит в промежутке времени между сериализацией и десериализацией объекта этого класса, поток ObjectInputStream способен обнаружить факт внесения изменений. При сохранении объекта вместе с ним записывается уникальный идентификатор номера версии – 64-битовое значение типа long. По умолчанию идентификатор создаётся в виде хеш-кода, построенного на основе информации об именах класса, его членов и базовых интерфейсов. Изменение таких данных служит сигналом о возможной несовместимости версий класса.

При вводе данных об объекте из потока ObjectInputStream считывается также и идентификатор номера версии. Затем предпринимается попытка загрузки соответствующего класса. Если требуемый класс не найден либо идентификатор загруженного класса не совпадает с тем, который считан из потока, метод readObject() выбрасывает исключение типа InvalidClassException.

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

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

private static final long serialVersionUID =...;

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

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

 



Поделиться:


Последнее изменение этой страницы: 2021-05-11; просмотров: 61; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.128.94.171 (0.005 с.)