Сериализация. JAVA Flashcards

1
Q

Что такое сериализация и как она реализована в Java?

A

Сериализация (Serialization) - процесс преобразования структуры данных в линейную последовательность байтов для дальнейшей передачи или сохранения. Сериализованные объекты можно затем восстановить (десериализовать).

В Java, согласно спецификации Java Object Serialization существует два стандартных способа сериализации: стандартная сериализация, через использование интерфейса Java.io.Serializable и «расширенная» сериализация - Java.io.Externalizable.

Сериализация позволяет в определенных пределах изменять класс. Вот наиболее важные изменения, с которыми спецификация Java Object Serialization может справляться автоматически:

для передачи объекта из одного процесса, который работает под управлением Java в другой процесс, например по сети для передачи состояния объекта между серверами.

Взять объект из памяти, далее запаковать в байт массив, и передать в другой процесс, через десереализацию, распаковку.

добавление в класс новых полей;
изменение полей из статических в нестатические;
изменение полей из транзитных в нетранзитные.
Обратные изменения (из нестатических полей в статические и из нетранзитных в транзитные) или удаление полей требуют определенной дополнительной обработки в зависимости от того, какая степень обратной совместимости необходима. Сериализация работает быстрее, чем JSON и XML, за счет того что это бинарные данные (байты).

Overall, the main reason for Java serialization is to provide a mechanism for transferring the state of objects between JVMs, or for storing objects for later use.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Для чего нужна сериализация?

A

1) Данные в памяти “живут”, до момента завершения приложения. Сериализация позволяет записать объекты в файлы и, таким образом, восстановить их затем.
2) Сериализация предоставляет универсальный механизм обмена данными между компонентами приложения или узлами в интернете.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Что нужно сделать, чтобы сериализовать объект?

A

1) Класс объекта должен реализовывать интерфейс Serializable
2) Создать поток ObjectOutputStream (oos), который записывает объект в переданный OutputStream.
3) Записать в поток: oos.writeObject(Object);
4) Сделать oos.flush() и oos.close()

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Что нужно сделать, чтобы ДЕсериализовать объект?

A

1) Класс объекта должен реализовывать интерфейс Serializable
2) Создать поток ObjectInputStream (ois), в который передать InputStream (instream) (из файла, из памяти…)
3) Получить объект и привести к нужному типу: MyObject my = (MyObject) ois.readObject(instream)

Для выполнения десериализации под объект выделяется память, после чего его поля
заполняются значениями из потока. Конструктор объекта при этом не вызывается. Однако
при десериализации будет вызван конструктор без параметров родительского
несериализуемого класса, а его отсутствие повлечет ошибку десериализации.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Перечислить шаги, которые выполняет механизм сериализации при сериализации объекта (что за чем записывается в поток)

A

1) Запись метаданных: указание, что это сериализация, версия сериализации, указание “сейчас начнется объект”.
2.1) Запись рекурсивно данных о классе объекта “снизу вверх”: класс -> super -> supersuper и т.д пока не будет достигнут java.lang.Object. Сам класс Object не пишется.
2.2) При описании пишется имя класса, имена и тип его полей, примитивных и ссылок. Для ссылок просто указывается, что “там объект”, без описания класса.
3.1) Запись значений данных в экземпляре, “сверху вниз”: parent -> sub -> subsub…
3.2) Если поле - ссылка на объект, то рекурсивно идет запись объекта: описания полей “снизу вверх”, затем написание значений “сверху вниз”.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Как изменить стандартное поведение сериализации/десериализации?

A

Реализовать интерфейс java.io.Externalizable, который позволяет применение
пользовательской логики сериализации. Способ сериализации и десериализации
описывается в методах writeExternal() и readExternal().
Во время десериализации вызывается
конструктор без параметров, а потом уже на созданном объекте вызывается метод
readExternal.
Если у сериализуемого объекта реализован(переопределен) один из следующих методов, то механизм
сериализации будет использовать его, а не метод по умолчанию :
* writeObject() – запись объекта в поток;
* readObject() – чтение объекта из потока;
* writeReplace() – позволяет заменить себя экземпляром другого класса перед
записью;
* readResolve() – позволяет заменить на себя другой объект после чтения.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Чем отличаются методы восстановления объекта в интерфейсах Serializable и Externalizable?

A

1) Serializible не использует конструктор объекта - он просто восстанавливает его полностью из последовательности байтов.
2.1) Externalizible вначале вызывает public конструктор объекта без параметров, а затем наполняет поля объекта значениями.
2.2) Если у класса не будет public конструктора без параметров, при попытке десериализовать объект выпадет java.io.InvalidClassException.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Чем отличаются интерфейсы Serializable и Externalizable?

A

1) Externalizable extends Serializable
2) Интерфейс Serializable полностью реализует виртуальная машина JVM.
3) Интерфейс Externalizable имеет методы writeExternal() и readExternal(), которые позволяют вручную указать - какие поля записывать. Это может в том числе повысит производительность.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Какие поля не будут сериализованы при сериализации? Будет ли
сериализовано final поле?

A

1) Добавить к полю модификатор transient. В таком случае после восстановления его значение будет null.

2) Сделать поле static. Значения статических полей автоматически не сохраняются.

3) Поля с модификатором final сериализуются как и обычные. За одним исключением – их невозможно десериализовать при использовании Externalizable, поскольку final-поля должны быть инициализированы в конструкторе, а после этого в readExternal изменить значение этого поля будет невозможно. Соответственно, если необходимо сериализовать объект с final-полем неоходимо использовать только стандартную сериализацию.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Как создать собственный протокол сериализации?

A

Для создания собственного протокола сериализации достаточно реализовать интерфейс Externalizable, который содержит два метода:

public void writeExternal(ObjectOutput out) throws IOException;

public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Какая роль поля serialVersionUID в сериализации?

A

serialVersionUID используется для указании версии сериализованных данных.

Когда мы не объявляем serialVersionUID в нашем классе явно, среда выполнения Java делает это за нас, но этот процесс чувствителен ко многим метаданным класса включая количество полей, тип полей, модификаторы доступа полей, интерфейсов, которые реализованы в классе и пр.
Рекомендуется явно объявлять serialVersionUID т.к. при добавлении, удалении атрибутов класса динамически сгенерированное значение может измениться и в момент выполнения будет выброшено исключение InvalidClassException.

Если мы не объявляем его явно, Java делает это за нас.

private static final long serialVersionUID = 20161013L;

Как легко сгенерировать serialVersionUID для класса?
Для этого есть утилита serialver, имеющаяся в JDK:
$ serialver -classpath . Hello
Hello: static final long SerialVersionUID = -4862926644813433707L;

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Когда стоит изменять значение поля serialVersionUID?

A

serialVersionUID нужно изменять при внесении в класс несовместимых изменений, например при удалении какого-либо его атрибута.

Чтобы не допустить автоматическую сериализацию можно переопределить private методы для создания исключительной ситуации NotSerializableException.
~~~
private void writeObject(ObjectOutputStream out) throws IOException {
throw new NotSerializableException();
}
private void readObject(ObjectInputStream in) throws IOException {
throw new NotSerializableException();
}
~~~
Любая попытка записать или прочитать этот объект теперь приведет к возникновению исключительной ситуации.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

В чем проблема сериализации Singleton?

A

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

Существует два способа избежать этого:

явный запрет сериализации.
определение метода с сигнатурой default/public/private/protected Object readResolve() throws ObjectStreamException, назначением которого станет возврат замещающего объекта вместо объекта, на котором он вызван.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Расскажите про клонирование объектов.

A

Использование оператора присваивания не создает нового объекта, а лишь копирует ссылку на объект. Таким образом, две ссылки указывают на одну и ту же область памяти, на один и тот же объект.

Для создания нового объекта с таким же состоянием используется клонирование объекта.

в Java, есть 3 способа клонирования объекта:

  1. С использованием интерфейса Cloneable;

Первый способ подразумевает, что вы будете использовать механизм так называемого «поверхностного клонирования» и сами позаботитесь о клонировании полей-объектов. Метод clone() в родительском классе Object является protected, поэтому требуется переопределение его с объявлением как public. Он возвращает экземпляр объекта с копированными полями-примитивами и ссылками. И получается что у оригинала и его клона поля-ссылки указывают на одни и те же объекты.

  1. С использованием конструктора клонирования объекта;

В классе описывается конструктор, который принимает объект этого же класса и инициализирует значениями его полей поля нового объекта.

  1. С использованием сериализации.

Он заключается в сохранении объекта в поток байтов с последующей эксгумацией его от туда.

  1. Фабричный метод - (Factory method), который представляет собой статический метод, возвращающий экземпляр своего класса.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

В чем отличие между поверхностным и глубоким клонированием?

A

Поверхностное копирование копирует настолько малую часть информации об объекте, насколько это возможно. По умолчанию, клонирование в Java является поверхностным, т.е. класс Object не знает о структуре класса, которого он копирует.

Клонирование такого типа осуществляется JVM по следующим правилам:

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

Глубокое копирование дублирует абсолютно всю информацию объекта:

Нет необходимости копировать отдельно примитивные данные;
Все члены ссылочного типа в оригинальном классе должны поддерживать клонирование. Для каждого такого члена при переопределении метода clone() должен вызываться super.clone();
Если какой-либо член класса не поддерживает клонирование, то в методе клонирования необходимо создать новый экземпляр этого класса и скопировать каждый его член со всеми атрибутами в новый объект класса, по одному.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Какой способ клонирования предпочтительней?

A

Наиболее безопасным и следовательно предпочтительным способом клонирования является использование специализированного конструктора копирования:

Отсутствие ошибок наследования (не нужно беспокоиться, что у наследников появятся новые поля, которые не будут склонированы через метод clone());
Поля для клонирования указываются явно;
Возможность клонировать даже final поля.

17
Q

Почему метод clone() объявлен в классе Object, а не в интерфейсе Cloneable?

A

чтобы создать через клон новый объект – через интерфейс.

Метод clone() объявлен в классе Object с указанием модификатора native, чтобы обеспечить доступ к стандартному механизму поверхностного копирования объектов.

Одновременно он объявлен и как protected, чтобы нельзя было вызвать этот метод у не переопределивших его объектов. Непосредственно интерфейс Cloneable является маркерным (не содержит объявлений методов) и нужен только для обозначения самого факта, что данный объект готов к тому, чтобы быть клонированным. Вызов переопределённого метода clone() у не Cloneable объекта вызовет выбрасывание CloneNotSupportedException.

18
Q

Как создать глубокую копию объекта? (2 способа)

A

Глубокое клонирование требует выполнения следующих правил:

-Нет необходимости копировать отдельно примитивные данные;

-Все классы-члены в оригинальном классе должны поддерживать клонирование. Для каждого члена класса должен вызываться super.clone() при переопределении метода clone();

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

1 Сериализация – это еще один способ глубокого копирования. Мы просто сериализуем нужный объект и десериализуем его. Очевидно, объект должен поддерживать интерфейс Serializable. Мы сохраняет объект в массив байт и потом прочитать из него.

2 При помощи библиотеки DeepCloneable

Глубокое клонирование с этой библиотекой сводится с двум строкам кода:

Cloner cloner = new Cloner();

DeepCloneable clone = cloner.deepClone(this);

  1. конструктор копирования
  2. перепределение clone
19
Q

Зачем может понадобиться вручную определять методы сериализации (read/writeObject, read/writeExternal)?

A

1) Чтобы сериализовать не все поля.
2) Чтобы повысить производительность.
3) Чтобы зашифровать данные в полях.

20
Q

Вы сериализовали объект, а затем переименовали поле. Что будет при восстановлении?

A

Если в объекте указан serialVersionUID, то объект восстановится, но значение в поле - нет. Оно будет инициализировано null, 0 и т.д.

21
Q

Что будет, если мы попробуем сериализовать класс, в котором не написано implements Serializable?

A

Код скомпилируется, но во время выполнения выкинет java.io.NotSerializableException