Книга « Эффективная Java» и другие источники дают довольно хорошее объяснение того, как и когда использовать метод readObject () при работе с сериализуемыми классами Java. С другой стороны, метод readResolve () остается загадкой. Практически все документы, которые я нашел, либо упоминают только один из двух, либо упоминают оба только по отдельности.
Вопросы, которые остались без ответа:
- В чем разница между двумя методами?
- Когда какой метод следует применять?
- Как следует использовать readResolve (), особенно с точки зрения возврата чего?
Надеюсь, вы сможете пролить свет на этот вопрос.
java
serialization
singleton
фураж
источник
источник
String.CaseInsensitiveComparator.readResolve()
Ответы:
readResolve
используется для замены объекта, прочитанного из потока. Единственное применение, которое я когда-либо видел для этого, - это принудительное использование синглтонов; при чтении объекта замените его экземпляром синглтона. Это гарантирует, что никто не сможет создать другой экземпляр путем сериализации и десериализации синглтона.источник
transient
поля.readResolve
используется для разрешения объекта после его чтения. Пример использования: возможно, объект содержит некоторый кеш, который может быть воссоздан из существующих данных и не требует сериализации; кэшированные данные могут быть объявленыtransient
иreadResolve()
могут быть восстановлены после десериализации. Для подобных вещей и предназначен этот метод.Serializable
говорится: «Классы, которым необходимо назначить замену, когда ее экземпляр считывается из потока, должны реализовывать этот [readResolve
] специальный метод ...».Пункт 90, Эффективная Java, 3-е издание охватывает
readResolve
иwriteReplace
для последовательных прокси - их основное применение. Примеры не записываютreadObject
иwriteObject
методы, потому что они используют сериализацию по умолчанию для чтения и записи полей.readResolve
вызывается послеreadObject
возврата (и наоборот,writeReplace
вызывается доwriteObject
и, вероятно, для другого объекта). Объект, возвращаемый методом, заменяетthis
объект, возвращаемый пользователю,ObjectInputStream.readObject
и любые дальнейшие обратные ссылки на объект в потоке. ОбаreadResolve
иwriteReplace
могут возвращать объекты одного или разных типов. Возвращение того же типа полезно в некоторых случаях, когда должны быть поляfinal
и либо требуется обратная совместимость, либо значения должны быть скопированы и / или проверены.Использование
readResolve
не приводит к принудительному применению свойства singleton.источник
readResolve можно использовать для изменения данных, сериализованных с помощью метода readObject. Например, xstream API использует эту функцию для инициализации некоторых атрибутов, которые не были в XML для десериализации.
http://x-stream.github.io/faq.html#Serialization
источник
readResolve предназначен для случаев, когда вам может потребоваться вернуть существующий объект, например, потому что вы проверяете дублирующиеся входные данные, которые следует объединить, или (например, в распределенных системах, согласованных в конечном итоге), потому что это обновление может появиться до того, как вы узнаете о любые старые версии.
источник
readObject () - это существующий метод в классе ObjectInputStream. во время чтения объекта во время десериализации метод readObject внутренне проверяет, имеет ли десериализуемый объект класса метод readResolve или нет, если метод readResolve существует, тогда он вызовет метод readResolve и вернет тот же пример.
Таким образом, цель написания метода readResolve - это хорошая практика для достижения чистого одноэлементного шаблона проектирования, когда никто не может получить другой экземпляр путем сериализации / десериализации.
источник
readResolve () обеспечит одноэлементный контракт во время сериализации.
Пожалуйста, обратитесь
источник
Когда сериализация используется для преобразования объекта, чтобы его можно было сохранить в файле, мы можем запустить метод readResolve (). Метод является частным и хранится в том же классе, объект которого извлекается во время десериализации. Это гарантирует, что после десериализации возвращаемый объект будет таким же, как был сериализован. То есть,
instanceSer.hashCode() == instanceDeSer.hashCode()
readResolve () не является статическим методом. After
in.readObject()
вызывается во время десериализации, он просто проверяет, что возвращаемый объект такой же, как и тот, который был сериализован, как показано ниже, в то время какout.writeObject(instanceSer)
Таким образом, это также помогает в реализации одноэлементного шаблона проектирования , потому что каждый раз возвращается один и тот же экземпляр.
источник
Я знаю, что этот вопрос действительно старый и на него есть принятый ответ, но, поскольку он всплывает очень высоко в поиске Google, я думал, что взвесу, потому что не предоставленный ответ охватывает три случая, которые я считаю важными - на мой взгляд, основное использование для них методы. Конечно, все предполагают, что на самом деле существует необходимость в настраиваемом формате сериализации.
Взять, к примеру, классы коллекций. Сериализация связанного списка или BST по умолчанию приведет к огромной потере места с очень небольшим увеличением производительности по сравнению с простой сериализацией элементов по порядку. Это еще более верно, если коллекция является проекцией или представлением - хранит ссылку на более крупную структуру, чем она предоставляет своим общедоступным API.
Если в сериализованном объекте есть неизменяемые поля, требующие настраиваемой сериализации, исходного решения
writeObject/readObject
недостаточно, поскольку десериализованный объект создается до чтения записанной части потокаwriteObject
. Возьмем эту минимальную реализацию связанного списка:Эта структура может быть сериализована путем рекурсивного написания
head
поля каждой ссылки, за которым следуетnull
значение. Однако десериализация такого формата становится невозможной:readObject
невозможно изменить значения полей-членов (теперь фиксированныеnull
). Вот параwriteReplace
/readResolve
:Прошу прощения, если приведенный выше пример не компилируется (или не работает), но, надеюсь, этого достаточно, чтобы проиллюстрировать мою точку зрения. Если вы считаете, что это очень надуманный пример, помните, что многие функциональные языки работают на JVM, и в их случае такой подход становится существенным.
Возможно, мы захотим десериализовать объект другого класса, чем мы написали в
ObjectOutputStream
. Это может быть в случае с представлениями, такими какjava.util.List
реализация списка, которая предоставляет фрагмент из более длинногоArrayList
. Очевидно, сериализация всего списка поддержки - плохая идея, и мы должны записывать только элементы из просматриваемого фрагмента. Однако зачем останавливаться на этом и иметь бесполезный уровень косвенного обращения после десериализации? Мы могли бы просто прочитать элементы из потока вArrayList
и вернуть их напрямую, вместо того, чтобы заключать их в наш класс представления.В качестве альтернативы выбор конструкции может быть аналогичным классом делегата, предназначенным для сериализации. Хорошим примером может быть повторное использование нашего кода сериализации. Например, если у нас есть класс построителя (аналогичный StringBuilder для String), мы можем написать делегат сериализации, который сериализует любую коллекцию, записывая в поток пустой построитель, за которым следуют размер коллекции и элементы, возвращаемые итератором коллекции. Десериализация будет включать чтение построителя, добавление всех впоследствии прочитанных элементов и возврат результата final
build()
от делегатовreadResolve
. В этом случае нам нужно будет реализовать сериализацию только в корневом классе иерархии коллекции, и не потребуется дополнительный код из текущих или будущих реализаций, при условии, что они реализуют абстрактныеiterator()
иbuilder()
метод (последний для воссоздания однотипной коллекции, что само по себе было бы очень полезной функцией). Другим примером может быть иерархия классов, код которой мы не полностью контролируем - наши базовые классы из сторонней библиотеки могут иметь любое количество частных полей, о которых мы ничего не знаем и которые могут меняться от одной версии к другой, нарушая наши сериализованные объекты. В этом случае было бы безопаснее записать данные и перестроить объект вручную при десериализации.источник
Метод readResolve
Для классов Serializable и Externalizable метод readResolve позволяет классу заменять / разрешать объект, считанный из потока, прежде чем он будет возвращен вызывающей стороне. Реализуя метод readResolve, класс может напрямую управлять типами и экземплярами своих десериализуемых экземпляров. Метод определяется следующим образом:
ANY-ACCESS-MODIFIER Объект readResolve () выдает исключение ObjectStreamException;
Метод readResolve вызывается, когда ObjectInputStream считывает объект из потока и готовится вернуть его вызывающей стороне. ObjectInputStream проверяет, определяет ли класс объекта метод readResolve. Если метод определен, вызывается метод readResolve, чтобы позволить объекту в потоке обозначить возвращаемый объект. Возвращаемый объект должен иметь тип, совместимый с любым использованием. Если он несовместим, при обнаружении несоответствия типов будет выброшено исключение ClassCastException .
Например, можно создать класс Symbol, для которого на виртуальной машине существовал только один экземпляр каждой привязки символа. Метод readResolve будет реализован, чтобы определить, был ли этот символ уже определен, и заменить существующий эквивалентный объект Symbol, чтобы сохранить ограничение идентичности. Таким образом, уникальность объектов Symbol может сохраняться в процессе сериализации.
источник
Как уже было сказано,
readResolve
это частный метод, используемый в ObjectInputStream при десериализации объекта. Это вызывается непосредственно перед возвратом фактического экземпляра. В случае синглтона здесь мы можем принудительно вернуть уже существующую ссылку на экземпляр синглтона вместо ссылки на десериализованный экземпляр. То же самое и сwriteReplace
ObjectOutputStream.Пример для
readResolve
:}
Вывод:
источник