Использование readObject() и readResolve() в Java: примеры

Пройдите тест, узнайте какой профессии подходите

Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Быстрый ответ

readObject() контролирует процесс десериализации, восстанавливая состояние объекта из входного потока:

Java
Скопировать код
private void readObject(ObjectInputStream stream)
    throws IOException, ClassNotFoundException {
    // Восстановление состояния объекта...
}

Метод readResolve() позволяет вмешаться в процесс по окончании десериализации, например, для обеспечения соблюдения шаблона Singleton или замены объекта на другой:

Java
Скопировать код
private Object readResolve() throws ObjectStreamException {
    // Возвращаем единственный экземпляр объекта
    return INSTANCE;
}

В общем случае, readObject() описывает как происходит десериализация, в то время как readResolve() определяет что в итоге будет получено.

Кинга Идем в IT: пошаговый план для смены профессии

Сценарии использования readResolve

Метод readResolve() предназначен для случаев, когда после десериализации важно выполнять определенные условия, такие как соблюдение шаблона Singleton или поддержание неизменности объекта. Например, при десериализации класса Singleton может быть создан новый экземпляр. Использование readResolve() позволяет вернуть существующий экземпляр Singleton вместо этого:

Java
Скопировать код
public class Singleton implements Serializable {
    public static final Singleton INSTANCE = new Singleton();

    protected Object readResolve() {
        // Гарантируем, что синглтон останется неизменным.
        return INSTANCE;
    }
}

readResolve() может также восстановить статус неизменности объекта с финализированными полями после десериализации.

Работа с readObject и readResolve

  • Предотвращение дублирования: readResolve() можно использовать для сопоставления десериализованного объекта с уже существующими и предотвратить таким образом создание дубликатов.
  • Обеспечение целостности: В распределенных системах readResolve() может обновить ссылки на объекты так, чтобы они указывали на актуальные версии объектов в разных узлах.
  • Инициализация объектов по своим правилам: С помощью readResolve() можно инициализировать несериализуемые поля с использованием внешних библиотек, чтобы обеспечить их корректное состояние.

Прокси-сериализация объяснена

В книге "Effective Java" описывается шаблон прокси-сериализации. Он использует writeReplace для сериализации замещающего объекта, который содержит логическое состояние исходного объекта. Метод readResolve() восстанавливает исходный объект из прокси после десериализации, что существенно улучшает безопасность процесса.

Java
Скопировать код
private Object writeReplace() {
    // Создаем заместителя.
    return new SerializationProxy(this);
}

private static class SerializationProxy implements Serializable {
    private final int data;

    SerializationProxy(YourClass yc) {
        this.data = yc.data;
    }

    private Object readResolve() {
        // Восстанавливаем исходный объект.
        return new YourClass(this.data);
    }
}

Глубокое погружение в readResolve

  • Вариативность возвращаемых типов: readResolve() может возвращать объект того же класса или объекта другого класса.
  • Момент вызова: readResolve() происходит на одном из заключительных этапов десериализации, что позволяет заменить объект еще до окончания восстановления его состояния.

Визуализация

Можно представить процесс сериализации в Java как приготовление блюда:

Markdown
Скопировать код
Запечатленный Объект ➡️ [Сериализация] ➡️ Хранение в Файле (🗃️)
Чтение из Файла (🗃️) ➡️ [Десериализация] ➡️ Восстановление Объекта (📖)

Особенности этих методов:

Java
Скопировать код
readObject()     // Ваш рецепт (👨‍🍳)
readResolve()    // Ваше уникальное дополнение (👩‍🍳🧂)

readObject() в точности воспроизводит первоначальную структуру объекта, как по рецепту.

readResolve() добавляет уникальное дополнение, совершенствуя итоговый результат:

Markdown
Скопировать код
Записанный Рецепт (🗃️) -> [Приготовление 👨‍🍳] -> Основное Блюдо (🍲) -> [Дополнение уникальной изюминкой 👩‍🍳🧂] -> Идеальное Блюдо (👌🍽️)

Распространённые ошибки

  • Неправильное использование readResolve: Применение readResolve(), когда это не требуется, может усложнить ваш код.
  • Перезапись переменных: readResolve() не должен переопределять финальные переменные.
  • Соответствие требованиям безопасности: Нужно контролировать данные, участвующие в десериализации вашего объекта, так чтобы не были использованы вредоносные данные.

Полезные материалы

  1. ObjectInputStream (Java Platform SE 7) — Официальная документация Oracle по readObject().
  2. Serializable (Java Platform SE 7) — Документация о интерфейсе Serializable.
  3. Java IO: Serializable — Глубокое подробное руководство по сериализации в Java и readResolve().
  4. Погружение в сериализацию Java — Подробно о сериализации в Java.
  5. Подробное руководство по сериализации в Java – CodeJava — Все об аспектах сериализации в Java.
Свежие материалы