1
1
Fork 0
java-interview/serialization.md

159 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[Вопросы для собеседования](README.md)
# Сериализация
+ [Что такое _«сериализация»_?](#Что-такое-сериализация)
+ [Опишите процесс сериализации/десериализации с использованием `Serializable`.](#Опишите-процесс-сериализациидесериализации-с-использованием-serializable)
+ [Как изменить стандартное поведение сериализации/десериализации?](#Как-изменить-стандартное-поведение-сериализациидесериализации)
+ [Как исключить поля из сериализации?](#Как-исключить-поля-из-сериализации)
+ [Что обозначает ключевое слово `transient`?](#Что-обозначает-ключевое-слово-transient)
+ [Какое влияние оказывают на сериализуемость модификаторы полей `static` и `final`](#Какое-влияние-оказывают-на-сериализуемость-модификаторы-полей-static-и-final)
+ [Как не допустить сериализацию?](#Как-не-допустить-сериализацию)
+ [Как создать собственный протокол сериализации?](#Как-создать-собственный-протокол-сериализации)
+ [Какая роль поля `serialVersionUID` в сериализации?](#Какая-роль-поля-serialversionuid-в-сериализации)
+ [Когда стоит изменять значение поля `serialVersionUID`?](#Когда-стоит-изменять-значение-поля-serialversionuid)
+ [В чем проблема сериализации Singleton?](#В-чем-проблема-сериализации-singleton)
+ [Какие существуют способы контроля за значениями десериализованного объекта](#Какие-существуют-способы-контроля-за-значениями-десериализованного-объекта)
## Что такое _«сериализация»_?
__Сериализация (Serialization)__ - процесс преобразования структуры данных в линейную последовательность байтов для дальнейшей передачи или сохранения. Сериализованные объекты можно затем восстановить (десериализовать).
В Java, согласно спецификации Java Object Serialization существует два стандартных способа сериализации: стандартная сериализация, через использование интерфейса `java.io.Serializable` и «расширенная» сериализация - `java.io.Externalizable`.
Сериализация позволяет в определенных пределах изменять класс. Вот наиболее важные изменения, с которыми спецификация Java Object Serialization может справляться автоматически:
+ добавление в класс новых полей;
+ изменение полей из статических в нестатические;
+ изменение полей из транзитных в нетранзитные.
Обратные изменения (из нестатических полей в статические и из нетранзитных в транзитные) или удаление полей требуют определенной дополнительной обработки в зависимости от того, какая степень обратной совместимости необходима.
[к оглавлению](#Сериализация)
## Опишите процесс сериализации/десериализации с использованием `Serializable`.
При использовании Serializable применяется алгоритм сериализации, который с помощью рефлексии (Reflection API) выполняет:
+ запись в поток метаданных о классе, ассоциированном с объектом (имя класса, идентификатор `SerialVersionUID`, идентификаторы полей класса);
+ рекурсивную запись в поток описания суперклассов до класса `java.lang.Object` (не включительно);
+ запись примитивных значений полей сериализуемого экземпляра, начиная с полей самого верхнего суперкласса;
+ рекурсивную запись объектов, которые являются полями сериализуемого объекта.
При этом ранее сериализованные объекты повторно не сериализуются, что позволяет алгоритму корректно работать с циклическими ссылками.
Для выполнения десериализации под объект выделяется память, после чего его поля заполняются значениями из потока. Конструктор объекта при этом не вызывается. Однако при десериализации будет вызван конструктор без параметров родительского несериализуемого класса, а его отсутствие повлечёт ошибку десериализации.
[к оглавлению](#Сериализация)
## Как изменить стандартное поведение сериализации/десериализации?
+ Реализовать интерфейс `java.io.Externalizable`, который позволяет применение пользовательской логики сериализации. Способ сериализации и десериализации описывается в методах `writeExternal()` и `readExternal()`. Во время десериализации вызывается конструктор без параметров, а потом уже на созданном объекте вызывается метод `readExternal`.
+ Если у сериализуемого объекта реализован один из следующих методов, то механизм сериализации будет использовать его, а не метод по умолчанию :
+ `writeObject()` - запись объекта в поток;
+ `readObject()` - чтение объекта из потока;
+ `writeReplace()` - позволяет заменить себя экземпляром другого класса перед записью;
+ `readResolve()` - позволяет заменить на себя другой объект после чтения.
[к оглавлению](#Сериализация)
## Как исключить поля из сериализации?
Для управления сериализацией при определении полей можно использовать ключевое слово `transient`, таким образом исключив поля из общего процесса сериализации.
[к оглавлению](#Сериализация)
## Что обозначает ключевое слово `transient`?
Поля класса, помеченные модификатором `transient`, не сериализуются.
Обычно в таких полях хранится промежуточное состояние объекта, которое, к примеру, проще вычислить. Другой пример такого поля - ссылка на экземпляр объекта, который не требует сериализации или не может быть сериализован.
[к оглавлению](#Сериализация)
## Какое влияние оказывают на сериализуемость модификаторы полей `static` и `final`
При стандартной сериализации поля, имеющие модификатор static, не сериализуются. Соответственно, после десериализации это поле значения не меняет. При использовании реализации `Externalizable` сериализовать и десериализовать статическое поле можно, но не рекомендуется этого делать, т.к. это может сопровождаться трудноуловимыми ошибками.
Поля с модификатором `final` сериализуются как и обычные. За одним исключением их невозможно десериализовать при использовании `Externalizable`, поскольку `final` поля должны быть инициализированы в конструкторе, а после этого в `readExternal()` изменить значение этого поля будет невозможно. Соответственно, если необходимо сериализовать объект с `final` полем необходимо использовать только стандартную сериализацию.
[к оглавлению](#Сериализация)
## Как не допустить сериализацию?
Чтобы не допустить автоматическую сериализацию можно переопределить `private` методы для создания исключительной ситуации `NotSerializableException`.
```java
private void writeObject(ObjectOutputStream out) throws IOException {
throw new NotSerializableException();
}
private void readObject(ObjectInputStream in) throws IOException {
throw new NotSerializableException();
}
```
Любая попытка записать или прочитать этот объект теперь приведет к возникновению исключительной ситуации.
[к оглавлению](#Сериализация)
## Как создать собственный протокол сериализации?
Для создания собственного протокола сериализации достаточно реализовать интерфейс `Externalizable`, который содержит два метода:
```java
public void writeExternal(ObjectOutput out) throws IOException;
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
```
[к оглавлению](#Сериализация)
## Какая роль поля `serialVersionUID` в сериализации?
`serialVersionUID` используется для указания версии сериализованных данных.
Когда мы не объявляем `serialVersionUID` в нашем классе явно, среда выполнения Java делает это за нас, но этот процесс чувствителен ко многим метаданным класса включая количество полей, тип полей, модификаторы доступа полей, интерфейсов, которые реализованы в классе и пр.
Рекомендуется явно объявлять `serialVersionUID` т.к. при добавлении, удалении атрибутов класса динамически сгенерированное значение может измениться и в момент выполнения будет выброшено исключение `InvalidClassException`.
```java
private static final long serialVersionUID = 20161013L;
```
[к оглавлению](#Сериализация)
## Когда стоит изменять значение поля `serialVersionUID`?
`serialVersionUID` нужно изменять при внесении в класс несовместимых изменений, например при удалении какого-либо его атрибута.
[к оглавлению](#Сериализация)
## В чем проблема сериализации Singleton?
Проблема в том что после десериализации мы получим другой объект. Таким образом, сериализация дает возможность создать Singleton еще раз, что недопустимо. Существует два способа избежать этого:
+ явный запрет сериализации.
+ определение метода с сигнатурой `(default/public/private/protected/) Object readResolve() throws ObjectStreamException`, назначением которого станет возврат замещающего объекта вместо объекта, на котором он вызван.
[к оглавлению](#Сериализация)
## Какие существуют способы контроля за значениями десериализованного объекта
Если есть необходимость выполнения контроля за значениями десериализованного объекта, то можно использовать интерфейс `ObjectInputValidation` с переопределением метода `validateObject()`.
```java
// Если вызвать метод validateObject() после десериализации объекта, то будет вызвано исключение InvalidObjectException при значении возраста за пределами 39...60.
public class Person implements java.io.Serializable,
java.io.ObjectInputValidation {
...
@Override
public void validateObject() throws InvalidObjectException {
if ((age < 39) || (age > 60))
throw new InvalidObjectException("Invalid age");
}
}
```
Так же существуют способы подписывания и шифрования, позволяющие убедиться, что данные не были изменены:
+ с помощью описания логики в `writeObject()` и `readObject()`.
+ поместить в оберточный класс `javax.crypto.SealedObject` и/или `java.security.SignedObject`. Данные классы являются сериализуемыми, поэтому при оборачивании объекта в `SealedObject` создается подобие «подарочной упаковки» вокруг исходного объекта. Для шифрования необходимо создать симметричный ключ, управление которым должно осуществляться отдельно. Аналогично, для проверки данных можно использовать класс `SignedObject`, для работы с которым также нужен симметричный ключ, управляемый отдельно.
[к оглавлению](#Сериализация)
# Источники
+ [IBM developerWorks](https://www.ibm.com/developerworks/ru/library/j-5things1/)
+ [Java-online.ru](http://java-online.ru/blog-serialization.xhtml)
+ [Изучите секреты Java Serialization API](http://ccfit.nsu.ru/~deviv/courses/oop/java_ser_rus.html)
+ [JavaRush](http://bit.ly/1xwRA2D)
+ [Записки трезвого практика](http://www.skipy.ru/technics/serialization.html)
[Вопросы для собеседования](README.md)