Использование readObject() и readResolve() в Java: примеры
Быстрый ответ
readObject()
контролирует процесс десериализации, восстанавливая состояние объекта из входного потока:
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
// Восстановление состояния объекта...
}
Метод readResolve()
позволяет вмешаться в процесс по окончании десериализации, например, для обеспечения соблюдения шаблона Singleton или замены объекта на другой:
private Object readResolve() throws ObjectStreamException {
// Возвращаем единственный экземпляр объекта
return INSTANCE;
}
В общем случае, readObject()
описывает как происходит десериализация, в то время как readResolve()
определяет что в итоге будет получено.
Сценарии использования readResolve
Метод readResolve()
предназначен для случаев, когда после десериализации важно выполнять определенные условия, такие как соблюдение шаблона Singleton или поддержание неизменности объекта. Например, при десериализации класса Singleton может быть создан новый экземпляр. Использование readResolve()
позволяет вернуть существующий экземпляр Singleton вместо этого:
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()
восстанавливает исходный объект из прокси после десериализации, что существенно улучшает безопасность процесса.
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 как приготовление блюда:
Запечатленный Объект ➡️ [Сериализация] ➡️ Хранение в Файле (🗃️)
Чтение из Файла (🗃️) ➡️ [Десериализация] ➡️ Восстановление Объекта (📖)
Особенности этих методов:
readObject() // Ваш рецепт (👨🍳)
readResolve() // Ваше уникальное дополнение (👩🍳🧂)
readObject()
в точности воспроизводит первоначальную структуру объекта, как по рецепту.
readResolve()
добавляет уникальное дополнение, совершенствуя итоговый результат:
Записанный Рецепт (🗃️) -> [Приготовление 👨🍳] -> Основное Блюдо (🍲) -> [Дополнение уникальной изюминкой 👩🍳🧂] -> Идеальное Блюдо (👌🍽️)
Распространённые ошибки
- Неправильное использование readResolve: Применение
readResolve()
, когда это не требуется, может усложнить ваш код. - Перезапись переменных:
readResolve()
не должен переопределять финальные переменные. - Соответствие требованиям безопасности: Нужно контролировать данные, участвующие в десериализации вашего объекта, так чтобы не были использованы вредоносные данные.
Полезные материалы
- ObjectInputStream (Java Platform SE 7) — Официальная документация Oracle по
readObject()
. - Serializable (Java Platform SE 7) — Документация о интерфейсе Serializable.
- Java IO: Serializable — Глубокое подробное руководство по сериализации в Java и
readResolve()
. - Погружение в сериализацию Java — Подробно о сериализации в Java.
- Подробное руководство по сериализации в Java – CodeJava — Все об аспектах сериализации в Java.