Центр практического обучения программированию.
  • (044) 237-37-07
  • (063) 237-37-07
Заказать звонок

Serializable vs. Externalizable

В данной статье рассматриваются и сравниваются два механизма сериализации объектов в Java из стандартной библиотеки JDK.

Сериализация ­ это процесс превращения состояния объекта (структуры данных) в последовательность бит для последующей передачи его по сети или сохранения в файл/базу данных. Процесс обратный к сериализации называется ­ десериализацией.

Стандартная библиотека JDK предоставляет разработчикам два способа сделать объект сериализируемым: интерфейсы
Serializable и Externalizable. Обьекты, классы которых реализуют один из этих интерфейсов, используются с классами ObjectOutputStream и ObjectInputStream, которые содержат методы writeObject (для сериализации) и readObject (для десериализации) соответственно.

Интерфейс Serializable.

Serializable ­ интерфейс ‘маркер’ (интерфейс не имеющий методов), используется при ‘автоматической’ сериализации объектов. В простейшем случаи, все что нужно ­ реализовать интерфейс Serializable, остальную работу по сериализации/десериализации выполнит JVM используя рефлексию для считывания/записи значений полей объекта. Статические и transient поля в поток сериализации не попадают.

Пример класса реализующего Serializable :

Рассмотрим подробнее данный класс:

Модификатор transient. Модификатором transient помечаются поля которые не должны участвовать в процессе сериализации, например если поле содержит некоторую конфиденциальную информацию (пароль, ключ и т.д.) или его значение проще вычислить чем сохранить. Также модификатором transient помечаются поля ссылочных типов, классы которых не реализуют интерфейс Serializable (AuthData в примере) и поэтому не могут быть сериализованы (в противном случае JVM бросает исключение java.io.NotSerializableException). При десериализации transient полям присваиваются значения по умолчанию. Также стоит отметить что применение модификатора transient имеет смысл только при использовании интерфейса Serializable.

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

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

Также стоит упомянуть что при десериализации объекта конструктор класса реализующего Serializable не вызывается, но будет вызван конструктор по умолчанию первого класса вверх по иерархии который не реализует этот интерфейс. И если такого конструктора нет, JVM бросает java.io.InvalidClassException: no valid constructor.

Существует механизм позволяющий обойти ограничения накладываемые ‘автоматической’ сериализацией. Например с его помощью можно сериализовать статические и transient поля. Для этого в класс реализующий интерфейс Serializable нужно добавить два приватных метода ­
readObject/writeObject и вручную описать процесс сериализации/десериализации.

Добавим такие методы в клсасс User для возможности сериализации класса AuthData:



Методы defaultWriteObject/defaulrReadObject выполняют автоматичкскую (де)сериализацию не статических и не transient полей (в случаи класса User ­ это поле ‘name’). Поле ’authData’ обрабатывается вручную: метод writeObject ­ записывает передаваемый обьект в поток сериализации, а метод readObject считывает объект из потока и возвращает ссылку на него.

Как было сказано выше, при использовании ‘автоматической’ сериализации, JVM считывает/записывает значение полей при помочи Reflection API, что не может не сказаться на производительности (особенно при обработке большого количества данных) этого подхода.

Интерфейс Externalizable

Externalizable ­ интерфейс (расширяет Serializable) имеющий два метода: writeExternal(ObjectOutput out) и readExternal(ObjectInput in), используется для ‘ручной’ сериализации состояния объекта. Под словом ‘ручная’ подразумевается то, что программист сам описывает (в методах writeExternal и readExternal) какие поля и в каком порядке будут сериализованы/десериализованы.

Класс User реализующий Externalizable:



Методы readExternal и writeExternal из класса AuthData:


Отличия от ‘автоматической’ серилизации:

Стоит также отметить тот факт что несмотря на преимущество в скорости этого механизма (в сравнении с ‘автоматической’ сериализацией), при обработке сложных объектных структур, ‘ручная’ сериализация должна быть тщательно продумана, во­избежании нарушения изначального графа объектов.

Выводы

Сравнивая два вышеописанных механизма можно сказать следующее:

P.S. Использование ObjectOutputStream и
ObjectInputStream

Независимо от выбранного интерфейса ( Serializable или Externalizable), код по работе с классами ObjectOutputStream и ObjectInputStream отличатся не будет:
Допустим у нас есть объект, класс которого реализует один из вышеуказанных интерфейсов:



Для сериализации и последующей записи этого обьекта в файл достаточно следующих нескольких строк:



Код для считывания данных из файла и десериализации их в объект выглядит следующим образом:


Остались вопросы?
Задать их менеджеру
Задать вопрос менеджеру
Введите свое имя и номер телефона и наш менеджер свяжится с вами!