Сериализация
Что такое сериализация и десериализация?
Сериализация - это процесс сохранения состояния объекта в последовательность байт. Десериализация - это процесс восстановления объекта из этих байт.
Что нужно сделать, чтобы ДЕсериализовать объект?
1) Класс объекта должен реализовывать интерфейс Serializable 2) Создать поток *ObjectInputStream* (ois), в который передать *InputStream* (instream) (из файла, из памяти...) 3) Получить объект и привести к нужному типу: MyObject my = (MyObject) *ois.readObject*(instream)
Какие объекты и методы используются при сериализации и де-сериализации в Java?
1) Интерфейс Serializable: void java.io.ObjectOutputStream.*writeObject*(object) Object java.io.ObjectInputStream.*readObject*() Также есть методы ручного чтения примитивных полей: readInt(), readFloat, writeByte и т.д. 2) Интерфейс Externalizable: void java.io.Externalizable.*readExternal*(ObjectInputStream in) void java.io.Externalizable.*writeExternal*(ObjectOutputStream out)
Чем отличаются интерфейсы Serializable и Externalizable?
1) Externalizable extends Serializable 2) Интерфейс Serializable полностью реализует виртуальная машина JVM. 3) Интерфейс Externalizable имеет методы writeExternal() и readExternal(), которые позволяют вручную указать - какие поля записывать. Это может в том числе повысит производительность.
Что такое serialVersionUID? Как он указывается? Что если его не определить?
1) SerialVersionUID - это "штамп" или код, который присваивается объекту при сериализации. Этот штамп позволяет отслеживать "версию" класса или объекта, который сериализуется или десериализуется. 2) Указывается в классе: *private static final long serialVersionId = ... * 3) Если SerialVersionId не задать явно, то при малейшем изменении класса (переименовали поле) оно изменится. 4) Если объект был сериализован, то теперь его не восстановить: его serialVersionId будет отличаться от сохраненного. Метод readObject() в этом случае только выдаст java.io.InvalidClassException.
Чем отличаются методы восстановления объекта в интерфейсах Serializable и Externalizable?
1) Serializible не использует конструктор объекта - он просто восстанавливает его полностью из последовательности байтов. 2.1) Externalizible вначале вызывает public конструктор объекта без параметров, а затем наполняет поля объекта значениями. 2.2) Если у класса не будет public конструктора без параметров, при попытке десериализовать объект выпадет java.io.InvalidClassException.
Какие поля в классе Java не сериализуются?
1) static поля - они часть класса, а не объекта. Следовательно, они не указывают на состояние объекта. 2) transient поля - модификатор как раз и говорит о том, что "это поле не отмечает состояние объекта". Следовательно, его не нужно сериализовать.
Что будет, если у нас класс Serializable, а его класс-родитель - нет?
1) В случае, если в цепочке классов (child -> parent -> grandparent) какой-то из них не Serializable, то он инициализируется *public конструктором без параметров*. 2) Даже если выше не-Serializable класса другие классы Serializable, это уже не важно - дальше создание идет просто по цепочке конструкторов. 3) Если в цепочке у какого-то класса не окажется конструктора без параметров - вылезет *java.io.InvalidClassException* с сообщением "no valid constructor".
А что если у нас Serializable класс содержит поля, в которых объекты не Serializable?
1) В таком случае код *скомпилируется*. 2) Но *в рантайме* при попытке сериализации , когда мы дойдем до этого поля и объекта, мы получим *NotSerializableException*.
Можно ли передавать сериализованный объект по сети интернет?
1) Да, сериализованный объект - это просто набор байтов. 2) Его спокойно можно передать по сети и в другом месте восстановить объект. 3) Также объект можно хранить в таком виде в базе данных или в файле.
Зачем вообще нужна сериализация?
1) Данные в памяти "живут", до момента завершения приложения. Сериализация позволяет записать объекты в файлы и, таким образом, восстановить их затем. 2) Сериализация предоставляет универсальный механизм обмена данными между компонентами приложения или узлами в интернете.
(Сериализация) Если у нас несколько объектов и мы хотим их сохранить в один файл - что мы делаем?
1) Делаем один FileOutputStream == fos 2) Оборачиваем его в ObjectOutputStream(fos) == oos 3) Пишем: oos.writeObject(object1); oos.writeObject(object2); ... oos.flush(); oos.close(); 4) Когда читаем объекты методом ObjectInputStream.readObject(), то получаем их в том же порядке, как они записаны.
Мы хотим, чтобы при сериализации значения некоторых полей не сохранялись. Как этого достичь?
1) Добавить к полю модификатор transient. В таком случае после восстановления его значение будет null. 2) Сделать поле static. Значения статических полей автоматически не сохраняются. Впрочем, это можно сделать вручную: ObjectOutputStream oos = new ...; oos.writeInt(obj.staticField1); oos.writeObject(a); 3) Также можно написать свои методы read/writeObject или read/writeExternal и записать в них только те поля, которые нам нужны.
Что за интерфейс Serializable?
1) Интерфейс java.io.Serializable - это интерфейс-маркер, в нем нет методов. 2) Если класс реализует интерфейс, это говорит сериализующему механизму, что этот класс можно сериализовать.
Вы взяли класс и сериализовали его объект. Затем вы добавили новое поле в классе. После чего - пытаетесь десериализовать объект. Что произойдет?
1) Если в классе не был указан serialVersionUID, то выпадет InvalidClassException, поскольку у класса он поменяется. 2) Если же был объявлен - объект восстановится. При этом восстановит значения в поля, что были до сериализации восстановятся, а новые инициализируются как 0, null и т.д.
Перечислит шаги, которые выполняет механизм сериализации при сериализации объекта (что за чем записывается в поток)
1) Запись метаданных: указание, что это сериализация, версия сериализации, указание "сейчас начнется объект". 2.1) Запись рекурсивно данных о классе объекта *"снизу вверх"*: класс -> super -> supersuper и т.д пока не будет достигнут java.lang.Object. *Сам класс Object не пишется.* 2.2) При описании пишется имя класса, имена и тип его полей, примитивных и ссылок. Для ссылок просто указывается, что "там объект", без описания класса. 3.1) Запись значений данных в экземпляре, *"сверху вниз"*: parent -> sub -> subsub... 3.2) Если поле - ссылка на объект, то рекурсивно идет запись объекта: описания полей "снизу вверх", затем написание значений "сверху вниз".
Что будет с работой интерфейсов Serizalizable и Externalizable в случае, если переменная - final?
1) Интерфейс Serializable отработает без проблем - он использует рефлексию для создания объекта. При этом используются методы ObjectInputStream.defaultReadObject() и ObjectOutputStream.defaultWriteObject(). 2) Однако, если мы создадим свои методы readObject() и writeObject(), то Serizalizable не сможет присвоить значение переменной: будет жаловаться, что она final. 3) У интерфейса Externalizable будет проблема в методе readExternal(). Он не сможет сделать this.field = in.readValue(). Потому что создает объект конструктором, а затем меняет значение.
Что нужно сделать, чтобы сериализовать объект?
1) Класс объекта должен реализовывать интерфейс Serializable 2) Создать поток *ObjectOutputStream* (oos), который записывает объект в переданный *OutputStream*. 3) Записать в поток: *oos.writeObject*(Object); 4) Сделать oos.flush() и oos.close()
Где ставится ключевое слово transient? Что оно означает?
1) Слово transient ("временный, переходный") - это модификатор для поля класса. 2) Если поле объявлено transient, это значит, что значение этого поля не нужно записывать при сериализации. 3) При восстановлении объекта у него будет это поле, но со значением null (0, false).
Зачем может понадобиться вручную определять методы сериализации (read/writeObject, read/writeExternal)?
1) Чтобы сериализовать не все поля. 2) Чтобы повысить производительность. 3) Чтобы зашифровать данные в полях.
Что такое совместимые и несовместимые изменения в механизме серилизации Java? Какие это изменения?
Compatible и incompatible changes - изменения в классе, после которых ранее сериализованные объекты еще можно как-то восстановить или нельзя. 1) Совместимые добавление, изменение, удаление поля или метода. 2) Несовместимые: изменение места класса в иерархии классов (например, extends другого родителя), удаление надписи implements Serializable.
Можно ли как-то вручную управлять процессом сериализации в Java?
Да, можно. Мы можем создать в классе методы: 1) *private void writeObject(ObjectOutputStream out) throws IOException* *private void readObject(ObjectInputStream in)t hrows IOException, ClassNotFoundException* Увидев эти методы, JVM будет вызывать их вместо стандартных. ВНИМАНИЕ: это *НЕ @Override!* Но это фича на уровне JVM. 2) Внутри этих методов можно вызвать "обычную" их реализацию, написав: out.defaulWriteObject(); in.defaultReadObject();
Как легко сгенерировать serialVersionUID для класса?
Для этого есть утилита *serialver*, имеющаяся в JDK: $ serialver -classpath . Hello Hello: static final long SerialVersionUID = -4862926644813433707L;
Вы сериализовали объект, а затем переименовали поле. Что будет при восстановлении?
Если в объекте указан serialVersionUID, то объект восстановится, но значение в поле - нет. Оно будет инициализировано null, 0 и т.д.
Что будет, если мы попробуем сериализовать класс, в котором не написано implements Serializable?
Код скомпилируется, но во время выполнения выкинет *java.io.NotSerializableException*
Что если мы наследовали Serizlizable класс, но хотим сделать его не-Serializable?
Нужно создать в классе собственные методы сериализации: writeObject() и readObject(). А в этих методах выкидывать исключение. Например: throw new NotSerializableException("Not serializable");
Мы записали в ObjectOutputStream out: out.writeObject(obj); out.writeInt(2); А затем в ObjectInputStream in вызываем: in.readInt(); in.readObject(); Что произойдет?
Поскольку записан объект, а мы пытаемся прочитать примитивный тип, то выпадет *java.io.OptionalDataException*. То же будет и если мы записали примитив, а считать хотим объект. А вот записать примитив и считать другой примитив можно.
Мы сериализовали объект класса Test и передали его на другую машину, где нет класса Test. Что будет, когда мы вызовем метод objectInputStream.readObject() ?
Поскольку класс не будет найден, будет выкинуто *java.lang.ClassNotFoundException*.