Цикл статей «Учебник Java 8».
Следующая статья — «Java 8 файлы (NIO.2)».
Предыдущая статья — «Java 8 потоки ввода/вывода».
Класс ObjectInputStream реализует интерфейс java.io.ObjectInput, а класс ObjectOutputStream реализует интерфейс java.io.ObjectOutput, в который описаны основные методы для чтения и записи объектов.
Чтение объекта из ObjectInputStream осуществляется с помощью метода
1 2 3 |
public final Object readObject() throws IOException, ClassNotFoundException |
Запись объекта в ObjectOutputStream производится с помощью метода:
1 2 |
public final void writeObject(Object obj) throws IOException |
Эти методы считывают и записывают в поток не только сам передаваемый объект, но и все объекты, на которые он ссылается, которые необходимы, для того чтобы восстановить исходный объект при чтении. Если какой-нибудь объект записывается в поток два раза, то реально он будет записан туда только один раз, но будет записано две ссылки на него. Если же какой-нибудь объект записывается в два разных потока, то при чтении из этих двух потоков получится два разных объекта.
Только классы, реализующие интерфейсы java.io.Serializable или java.io.Externalizable, могут быть считаны из потока и записаны в поток.
Метод readObject возвращает тип Object , который должен быт приведён к ожидаемому типу с помощью операции приведения типов. Строки и массивы в Java являются объектами. Примитивные типы считываются с помощью методов DataInput ( ObjectInput наследуется от DataInput ) и записываются с помощью методов DataOutput ( ObjectOutput наследуется от DataOutput ).
Запись объекта называется сериализацией.
Чтение объекта называется десериализацией.
Поля, объявленные как transient или static, игнорируются в процессе сериализации/десериализации.
При десериализации всегда создаются новые объекты, чтобы защитить уже существующие объекты от изменения.
В процессе чтения объекты создаются аналогично выполнению конструктора для нового объекта. Сначала выделяется память и заполняется нулями. Для несериализуемых классов вызываются конструкторы по умолчанию, а поля сериализуемых классов считываются из потока, начиная от наиболее близкого к java.lang.Object класса и заканчивая наиболее специфичным классом.
Если классу нужна особая обработка в процессе сериализации/десериализации, то он должен реализовать методы:
1 2 3 4 5 6 |
private void writeObject(java.io.ObjectOutputStream stream) throws IOException; private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException; private void readObjectNoData() throws ObjectStreamException; |
Метод 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 означает версию класса. Оно объявляется так:
1 |
ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L; |
Если в сериализуемом классе не объявлено поля serialVersionUID, то его значение вычисляется автоматически на основе окружения и полей класса. При чтении класса проверяется serialVersionUID. Если serialVersionUID считываемого класса не равен serialVersionUID нашего класса, то возникает исключение java.io.InvalidClassException. Старайтесь всегда объявлять вручную serialVersionUID, так как автоматически вычисленное значение может сильно отличаться в зависимости от платформы и реализации.
Класс, для которого при сериализации нужно использовать другой объект, должен реализовать метод:
1 |
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException; |
Метод writeReplace() вызывается в процессе сериализации, если он существует и доступен.
Если класс должен использовать другой объект при десериализации, то он должен реализовать метод:
1 |
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException; |
Метод readResolve() вызывается в процессе десериализации, если он существует и доступен.
Цикл статей «Учебник Java 8».
Следующая статья — «Java 8 файлы (NIO.2)».
Предыдущая статья — «Java 8 потоки ввода/вывода».
Просьба проверить остальное. Вот что найдено
в который описаны -> в котором описаны
быт приведён -> быть приведён
writeObject , приводит -> writeObject, приводит (пробел пред запятой есть ещё несколько случаев, нужно воспользоваться автозаменой)
ObjectInputStream -ом -> ObjectInputStream-ом