Программирование на Java

       

Стандартная сериализация


Для представления объектов в виде последовательности байт определены унаследованные от DataInput и DataOutput интерфейсы ObjectInput и ObjectOutput, соответственно. В java.io имеются реализации этих интерфейсов – классы ObjectInputStream и ObjectOutputStream.

Эти классы используют стандартный механизм сериализации, который предлагает JVM. Для того, чтобы объект мог быть сериализован, класс, от которого он порожден, должен реализовывать интерфейс java.io.Serializable. В этом интерфейсе не определен ни один метод. Он нужен лишь для указания, что объекты класса могут участвовать в сериализации. При попытке сериализовать объект, не имеющий такого интерфейса, будет брошен java.io.NotSerializableException.

Чтобы начать сериализацию объекта, нужен выходной поток OutputStream, в который и будет записываться сгенерированная последовательность байт. Этот поток передается в конструктор ObjectOutputStream. Затем вызовом метода writeObject() объект сериализуется и записывается в выходной поток. Например:

ByteArrayOutputStream os = new ByteArrayOutputStream(); Object objSave = new Integer(1); ObjectOutputStream oos = new ObjectOutputStream(os); oos.writeObject(objSave);

Чтобы увидеть, во что превратился объект objSave, можно просмотреть содержимое массива:

byte[] bArray = os.toByteArray();

А чтобы восстановить объект, его нужно десериализовать из этого массива:

ByteArrayInputStream is = new ByteArrayInputStream(bArray); ObjectInputStream ois = new ObjectInputStream(is); Object objRead = ois.readObject();

Теперь можно убедиться, что восстановленный объект идентичен исходному:

System.out.println("readed object is: " + objRead.toString()); System.out.println("Object equality is: " + (objSave.equals(objRead))); System.out.println("Reference equality is: " + (objSave==objRead));

Результатом выполнения приведенного выше кода будет:

readed object is: 1 Object equality is: true Reference equality is: false

Как мы видим, восстановленный объект не совпадает с исходным (что очевидно – ведь восстановление могло происходить и на другой машине), но равен сериализованному по значению.

Как обычно, для упрощения в примере была опущена обработка ошибок. Однако, сериализация (десериализация) объектов довольно сложная процедура, поэтому возникающие сложности не всегда очевидны. Рассмотрим основные исключения, которые может генерировать метод readObject() класса ObjectInputStream.

Предположим, объект некоторого класса TestClass был сериализован и передан по сети на другую машину для восстановления. Может случиться так, что у считывающей JVM на локальном диске не окажется описания этого класса (файл TestClass.class). Поскольку стандартный механизм сериализации записывает в поток байт лишь состояние объекта, для успешной десериализации необходимо наличие описание класса. В результате будет брошено исключение ClassNotFoundException.

Причина появления java.io.StreamCorruptedException вполне очевидна из названия – неправильный формат входного потока. Предположим, происходит попытка считать сериализованный объект из файла. Если этот файл испорчен (для эксперимента можно открыть его в текстовом редакторе и исправить несколько символов), то стандартная процедура десериализации даст сбой. Эта же ошибка возникнет, если считать некоторое количество байт (с помощью метода read) непосредственно из надстраиваемого потока InputStream. В таком случае ObjectInputStream снова обнаружит сбой в формате данных и будет брошено исключение java.io.StreamCorruptedException.

Поскольку ObjectOutput наследуется от DataOutput, ObjectOutputStream может быть использован для последовательной записи нескольких значений как объектных, так и примитивных типов в произвольной последовательности. Если при считывании будет вызван метод readObject, а в исходном потоке следующим на очереди записано значение примитивного типа, будет брошено исключение java.io.OptionalDataException. Очевидно, что для корректного восстановления данных из потока их нужно считывать именно в том порядке, в каком были записаны.



Содержание раздела