Проект

Общее

Профиль

Действия

SERIALIZATION » История » Редакция 1

Редакция 1/2 | Следующее »
Александр Александров, 21.04.2019 17:05


SERIALIZATION

Вопросы

  1. Что такое сериализация?
  2. Как исключить поля из сериализации?
  3. transient что значит?
  4. Как изменить стандартное поведение сериализации/десериализации?
  5. Вы создали класс, чей суперкласс сериализуемый, но при этом вы не хотите чтобы ваш класс был сериализуемым, как остановить сериализацию?
  6. Как создать собственный протокол сериализации?
  7. Какая роль поля serialVersionUID в сериализации?
  8. В чем проблема сериализации Singleton-ов?

Ответы

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

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

Как исключить поля из сериализации?

Для того чтоб исключить поля из сериализуемого потока, необходимо пометить поле модификатором transient.

transient что значит?

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

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

В большинстве случаев мы не определяем поведение вручную, а полагаемся на стандартную реализацию, и очень не удобно постоянно переопределять какие-то методы сериализации + постоянно следить за добавлением новых полей, добавлять их в методы. Ну и специально для этих целей есть Externalizable.

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

private void writeObject(ObjectOutputStream out) throws IOException;
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;

Обратите внимание, что оба метода объявлены как private, поскольку это гарантирует что методы не будут переопределены или перезагружены. Весь фокус в том, что виртуальная машина при вызове соответствующего метода автоматически проверяет, не были ли они объявлены в классе объекта. Виртуальная машина в любое время может вызвать private методы вашего класса, но другие объекты этого сделать не смогут. Таким образом обеспечивается целостность класса и нормальная работа протокол сериализации.

Вы создали класс, чей суперкласс сериализуемый, но при этом вы не хотите чтобы ваш класс был сериализуемым, как остановить сериализацию?

Вы не можете "разреализовать" интерфейс, поэтому если суперкласс реализует Serializable, то и созданный вами новый класс также будет реализовать его. Чтобы остановить автоматическую сериализацию вы можете применить private методы для создания исключительной ситуации NotSerializableException. Вот как это можно сделать:

prvate void writeObject(ObjectOutputStream out) throws IOException {
    throw new NotSerializableException("Not today!");
}

prvate void readObject(ObjectInputStream in) throws IOException {
    throw new NotSerializableException("Not today!");
}

Любая попытка записать или прочитать этот объект теперь приведет к возникновению исключительной ситуации. Запомните, если методы объявлены как private, никто не сможет модифицировать ваш код не изменяя исходный код класса. Java не позволяет переопределять такие методы.

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

Вместо реализации интерфейса Serializable, вы можете реализовать интерфейс Externalizable, который содержит два метода:

public void writeExternal(ObjectOutput out) throws IOException;
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;

Для создания собственного протокола нужно просто переопределить эти два метода. В отличие от двух других вариантов сериализации, здесь ничего не делается автоматически. Протокол полностью в ваших руках. Хотя это и наиболее сложный способ, при этом он наиболее контролируемый.

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

Поле private static Ùnal long serialVersionUID содержит уникальный идентификатор версии сериализованного класса. Оно вычисляется по содержимому класса - полям, их порядку объявления, методам, их порядку объявления. Соответственно, при любом изменении в классе это поле поменяет свое значение. Это поле записывается в поток при сериализации класса. Кстати, это, пожалуй, единственный известный случай, когда static-поле сериализуется.

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

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

ANY-ACCESS-MODIFIER Object readResolve() throws ObjectSTreamException

Модификатор доступа может быть private, protected и по умолчанию (default). Можно, наверное, сделать его и public, но смысла я в этом не вижу. Назначение этого метода - возвращать замещающий объект вместо объекта, на котором он вызван.

Обновлено Александр Александров больше 5 лет назад · 1 изменени(я, ий)

Go to top