Java 8 сериализация

Цикл статей «Учебник Java 8».

Следующая статья — «Java 8 файлы (NIO.2)».
Предыдущая статья — «Java 8 потоки ввода/вывода».

Класс ObjectInputStream  реализует интерфейс java.io.ObjectInput, а класс ObjectOutputStream  реализует интерфейс java.io.ObjectOutput, в который описаны основные методы для чтения и записи объектов.

Чтение объекта из ObjectInputStream  осуществляется с помощью метода

Запись объекта в ObjectOutputStream  производится с помощью метода:

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

Только классы, реализующие интерфейсы java.io.Serializable  или java.io.Externalizable, могут быть считаны из потока и записаны в поток.

Метод readObject  возвращает тип Object , который должен быт приведён к ожидаемому типу с помощью операции приведения типов. Строки и массивы в Java являются объектами. Примитивные типы считываются с помощью методов DataInput  ( ObjectInput  наследуется от DataInput ) и записываются с помощью методов DataOutput  ( ObjectOutput  наследуется от DataOutput ).

Запись объекта называется сериализацией.

Чтение объекта называется десериализацией.

Поля, объявленные как transient  или static, игнорируются в процессе сериализации/десериализации.

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

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

Если классу нужна особая обработка в процессе сериализации/десериализации, то он должен реализовать методы:

Метод readObject  отвечает за чтение и восстановление состояния объекта класса, записанного в поток соответствующим методом writeObject. Метод не должен беспокоиться о состоянии, принадлежащем его суперклассам или подклассам. Состояние восстанавливается чтением данных из ObjectInputStream  для каждого поля и присвоением этих значений соответствующим полям объекта. Чтение примитивных типов происходит с помощью DataInput.

Любая попытка считать данные, которые выходят за границу данных, записанных методом writeObject , приводит к исключению OptionalDataException. Методы чтения примитивных типов или в массив байт возвращают -1 или бросают исключение EOFException  в зависимости от метода.

Метод readObjectNoData  отвечает за инициализацию состояния объекта в случае, когда поток сериализации не имеет данного класса в качестве суперкласса объекта, десериализация которого происходит. Это может возникнуть, когда при сериализации использовалась другая версия класса, которая не имела в качестве суперкласса того класса, от которого наследуется класс у считывающей стороны. Эта ситуация может также возникнуть в случае подделки сериализованного потока. Метод readObjectNoData  полезен для инициализации объектов «враждебного» или повреждённого потока.

Сериализация не читает и не присваивает значения полям, которые не реализуют интерфейс java.io.Serializable. Подклассы класса Object, которые не сериализуемые, могут быть сериализуемыми, для этого они должны иметь конструктор по умолчанию. В этом случае задача сохранения и восстановления их состояния ложится на дочерний класс.

Любое исключение в процессе десериализации перехватывается ObjectInputStream -ом и останавливает процесс чтения.

Реализация интерфейса Externalizable  позволяет полностью контролировать содержимое и формат сериализации. Методы void writeExternal(ObjectOutput out) throws IOException  и void readExternal(ObjectInput in) throws IOException, ClassNotFoundException  интерфейса Externalizable  вызываются при сохранении и восстановлении состояния объектов.  При реализации каким-нибудь классом они могут записывать и считывать своё состояние с помощью методов ObjectOutput  и ObjectInput. Ответственность за обработку версионности ложится на сам объект.

Десериализация констант перечислений отличается от обычной сериализации и десериализации объектов. Сериализованная форма констант содержит только и имена, Значения полей констант опускаются. При десериализации константы ObjectInputStream  считывает имя константы из потока, затем вызывает метод Enum.valueOf(Class, String)  для получения значения. Процесс десериализации перечислений не может быть изменён. Любой метод readObject , readObjectNoData  и readResolve , объявленный в перечислении игнорируется.  Так же игнорируются поля serialPersistentFields  и serialVersionUID , перечисления имеют фиксированный serialVersionUID  0L.

Статическое поле serialVersionUID  означает версию класса. Оно объявляется так:

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

Класс, для которого при сериализации нужно использовать другой объект, должен реализовать метод:

Метод writeReplace() вызывается в процессе сериализации, если он существует и доступен.

Если класс должен использовать другой объект при десериализации, то он должен реализовать метод:

Метод readResolve() вызывается в процессе десериализации, если он существует и доступен.

Цикл статей «Учебник Java 8».

Следующая статья — «Java 8 файлы (NIO.2)».
Предыдущая статья — «Java 8 потоки ввода/вывода».

Один комментарий к “Java 8 сериализация”

  1. Просьба проверить остальное. Вот что найдено
    в который описаны -> в котором описаны
    быт приведён -> быть приведён
    writeObject , приводит -> writeObject, приводит (пробел пред запятой есть ещё несколько случаев, нужно воспользоваться автозаменой)
    ObjectInputStream -ом -> ObjectInputStream-ом

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *