В чем проблема сериализации singleton
Перейти к содержимому

В чем проблема сериализации singleton

  • автор:

Singleton serialization или сад камней

image

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

Но зачем городить огород сад?

Первый логичный вопрос: если так проблем то зачем это вообще нужно? Такая хитрость, действительно, требуется не часто. Хотя многие люди, только начинающие работу с WPF или WinForms, пытаются реализовать таким образом файл с настройками приложения. Пожалуйста, не тратьте свое время и не изобретайте велосипед: для этого есть Application и User Settings (почитать про это можно здесь и здесь). Вот примеры, когда сериализация может потребоваться:
Хочется передать синглтон по сети или между AppDomain. К примеру, клиент и сервер одновременно работают с одним и тем же ftp и синхронизируют свои сведения о нем. Информацию об ftp можно хранить в синглтоне (и там же пристроить методы для работы с ним).
Сериализуется класс, которой присваивается различным элементам, но значение должно быть одинаковым для всех. Примером такого класса может являться DBNull.

Синглтон

В качестве несложного примера возьмем такой синглтон:

public sealed class Settings : ISerializable < private static readonly Settings Inst = new Settings(); private Settings() < >public static Settings Instance < get < return Inst; >> public String ServerAddress < get < return _servAddr; >set < _servAddr = value; >> public String Port < get < return _port; >set < _port = value; >> private String _port = ""; > 
  • Намерено отсутствуют ленивые вычисления, чтобы не усложнять код.
  • Свойства не могут быть сделаны автоматическими, т.к. имена скрытых полей класса генерируются снова при каждой компиляции, и однажды честно сериализованный объект может уже не десериализоваться по причине различия этих имен.
Первый взгляд

В простых случаях для сериализации в C# хватает добавить атрибут Serializable. Что ж, не будем сильно задумываться на сколько наш случай сложен и добавим этот атрибут. Теперь попробуем сериализовать наш синглтон в трех вариантах: SOAP, Binary и обычный XML.
Для примера сериализуем и десериализуем бинарно (остальные способы аналогичны):

using (var mem = new MemoryStream())

(Не)ожиданно на консоль будет выведено false, а это значит, что мы получили два объекта-синглтона. Такой результат можно предвидеть, если вспомнить, что в процессе десериализации с помощью рефлексии вызывается приватный конструктор и все десериализуемые значения присваиваются новому объекту. UPD: Как справедливо заметил kekekeks будет вызван не приватный конструктор, а

BinaryFormatter использует FormatterServices.GetSafeUninitializedObject, который позволяет создать экземпляр объекта без вызова конструктора.

Именно эта особенность синглтона кладет первый камень в наш сад: синглтон перестает быть синглтоном.

Усложняем и… кладем еще каменей.

Так как не получилось сделать все просто, придется усложнить Если обратимся к более “ручному” процессу сериализации через интерфейс ISerializable, то на первый взгляд выгоды кажется никакой: прошлая беда не исчезла, а сложность возросла. Поэтому для дальнейшей действий нам еще потребуется достаточно редко используемый интерфейс IObjectReference. Все что он делает: показывает что объект класса, реализующего этот интерфейс, указывает на другой объект. Звучит странно, не правда ли? Но нам нужна другая особенность: после десериализации такого объекта будет возвращен указатель не на него самого, а на тот объект, на который он указывает. В нашем случае логично было бы возвращать указатель на синглтон. Класс будет выглядеть так:

[Serializable] internal sealed class SettingsSerializationHelper : IObjectReference < public Object GetRealObject(StreamingContext context) < return Settings.Instance; >> 
  • Перед тем как сериализовать синглтон требуется создать объект другого класса.
  • Поля синглтона по-прежнему не сериализуются.
public void GetObjectData(SerializationInfo info, StreamingContext context)

Теперь когда мы будем сериализовывать синглтон вместо него будет сохранен объект SettingsSerializationHelper, а при десериализации мы получим обратно наш синглтон. Проверив вывод на консоль из ранее описанного примера сериализации, мы увидим, что в случае с Binary и SOAP будет выведено на консоль true, но для XML сериализации — false. Следовательно, XMLSerializer не вызывает GetObjectData и просто самостоятельно обрабатывает все public поля/свойства.

Грязные хаки

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

public void GetObjectData(SerializationInfo info, StreamingContext context)
  • Полностью повторить все поля синглтона в SettignsSerializationHelper. Такую подмену десериализатор вполне скушает, заполнит все поля, а внутри метода GetRealObject их надо обратно присвоить синглтону. У такого подхода есть один большой и серьёзный недостаток: ручная поддержка дублирования полей, их добавление для сериализации и десериализации. Это явно не наш бро выбор.
  • Призвать на помощь рефлексию, суррогатный селектор и чуточку linq, чтобы все было сделано за нас. Рассмотрим это подробнее.
public void GetObjectData(SerializationInfo info, StreamingContext context) < info.SetType(typeof (SettignsSerializeHelper)); var fields = from field in typeof (Settings).GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) where field.GetCustomAttribute(typeof (NonSerializedAttribute)) == null select field; foreach (var field in fields) < info.AddValue(field.Name, field.GetValue(Settings.Instance)); >> 

Отлично, теперь когда мы захотим добавить поле в синглтон оно будет тоже сериалзованно без работы руками. Перейдем к десериализации.
Все поля синглтона должны быть повторены в SettignsSerializationHelper, но для того, чтобы избежать их реального дублирования, применим суррогатный селектор и изменим SettignsSerializationHelper.
Новый SettignsSerializationHelper:

[Serializable] internal sealed class SettignsSerializeHelper : IObjectReference < public readonly Dictionaryinfos = (from field in typeof (Settings).GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) where field.GetCustomAttribute(typeof (NonSerializedAttribute)) == null select field).ToDictionary(x => x.Name, x => new object()); public object GetRealObject(StreamingContext context) < foreach (var info in infos) < typeof (Settings).GetField(info.Key, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).SetValue(Settings.Instance, info.Value); >return Settings.Instance; > > 

И так, внутри SettignsSerializationHelper создается хэш-мап, где key — имена сериализуемых полей, а value в будущем станут значениями этих полей после десериалазации. Здесь для большей инкапсуляции можно сделать infos как private и написать метод для доступка к его key-value парам, но мы не будем усложнять пример. Внутри GetRealObject мы устанавливаем синглтону его десериализованные значения полей и возвращаем ссылку на него.
Теперь осталось только заполнить infos значениями полей. Для этого будет использован селектор.

internal sealed class SettingsSurrogate : ISerializationSurrogate < public void GetObjectData(object obj, SerializationInfo info, StreamingContext context) < throw new NotImplementedException(); >public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) < var ssh = new SettignsSerializeHelper(); foreach (var val in info) < ssh.infos[val.Name] = val.Value; >return ssh; > > 

Так как селектор будет использоваться только для десериализации, то мы напишем только SetObjectData. Когда obj (десериализуемый объект) приходит внутрь селектора, его поля заполнены 0 и null не зависимо от обстоятельств (obj получается после вызова в процессе десериализации метода GetUninitializedObject из FormatterServices). Поэтому в нашем случае проще создать новый SettignsSerializationHelper и вернуть его (этот объект будет считаться десериализованным). Далее, внутри foreach заполняем infos десериализованными данными, которые потом будут присвоены полям синглтона.
И теперь пример самого процесса сериализации/десериализации:

И теперь пример самого процесса сериализации/десериализации: using (var mem = new MemoryStream())

На консоль будет выведено true и все поля будут восстановлены. Наконец, мы закончили и привели наш сад камней в должный вид.

Ответы на вопросы на собеседование Сериализация.

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

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

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

  • transient что значит?

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

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

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

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

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

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

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

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

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

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

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

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

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

Это поле записывается в поток при сериализации класса. Кстати, это, пожалуй, единственный известный случай, когда static-поле сериализуется.

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

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

Решение же заключается в следующем. В классе определяется метод со следующей сигнатурой

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

Подводные камни Singleton: почему самый известный шаблон проектирования нужно использовать с осторожностью

Обложка поста Подводные камни Singleton: почему самый известный шаблон проектирования нужно использовать с осторожностью

Паттерн «Одиночка» — пожалуй, самый известный паттерн проектирования. Тем не менее, он не лишен недостатков, поэтому некоторые программисты (например, Егор Бугаенко) считают его антипаттерном. Разбираемся в том, какие же подводные камни таятся в Singleton’е.

Определение паттерна

Само описание паттерна достаточно простое — класс должен гарантированно иметь лишь один объект, и к этому объекту должен быть предоставлен глобальный доступ. Скорее всего, причина его популярности как раз и кроется в этой простоте — всего лишь один класс, ничего сложного. Это, наверное, самый простой для изучения и реализации паттерн. Если вы встретите человека, который только что узнал о существовании паттернов проектирования, можете быть уверены, что он уже знает про Singleton. Проблема заключается в том, что когда из инструментов у вас есть только молоток, всё вокруг выглядит как гвозди. Из-за этого «Одиночкой» часто злоупотребляют.

Простейшая реализация

Как уже говорилось выше, в этом нет ничего сложного:

  • Сделайте конструктор класса приватным, чтобы не было возможности создать экземпляр класса извне.
  • Храните экземпляр класса в private static поле.
  • Предоставьте метод, который будет давать доступ к этому объекту.
public class Singleton < private static Singleton instance = new Singleton(); private Singleton() < >public static Singleton getInstance() < return instance; >> 

Принцип единственной обязанности

В объектно-ориентированном программировании существует правило хорошего тона — «Принцип едиственной обязанности» (Single Responsibility Principle, первая буква в аббревиатуре SOLID). Согласно этому правилу, каждый класс должен отвечать лишь за один какой-то аспект. Совершенно очевидно, что любой Singleton-класс отвечает сразу за две вещи: за то, что класс имеет лишь один объект, и за реализацию того, для чего этот класс вообще был создан.

Принцип единственной обязанности был создан не просто так — если класс отвечает за несколько действий, то, внося изменения в один аспект поведения класса, можно затронуть и другой, что может сильно усложнить разработку. Так же разработку усложняет тот факт, что переиспользование (reusability) класса практически невозможно. Поэтому хорошим шагом было бы, во-первых, вынести отслеживание того, является ли экземпляр класса единственным, из класса куда-либо во вне, а во-вторых, сделать так, чтобы у класса, в зависимости от контекста, появилась возможность перестать быть Singleton’ом, что позволило бы использовать его в разных ситуациях, в зависимости от необходимости (т.е. с одним экземпляром, с неограниченным количество экземпляров, с ограниченным набором экземпляров и так далее).

Тестирование

Один из главных минусов паттерна «Одиночка» — он сильно затрудняет юнит-тестирование. «Одиночка» привносит в программу глобальное состояние, поэтому вы не можете просто взять и изолировать классы, которые полагаются на Singleton. Поэтому, если вы хотите протестировать какой-то класс, то вы обязаны вместе с ним тестировать и Singleton, но это ещё полбеды. Состояние «Одиночки» может меняться, что порождает следующие проблемы:

  • Порядок тестов теперь имеет значение;
  • Тесты могут иметь нежелательные сторонние эффекты, порождённые Singleton’ом;
  • Вы не можете запускать несколько тестов параллельно;
  • Несколько вызовов одного и того же теста могут приводить к разным результатам.

На эту тему есть отличный доклад с «Google Tech Talks»:

Скрытые зависимости

Обычно, если классу нужно что-то для работы, это сразу понятно из его методов и конструкторов. Когда очевидно, какие зависимости есть у класса, гораздо проще их предоставить. Более того, в таком случае вы можете использовать вместо реально необходимых зависимостей заглушки для тестирования. Если же класс использует Singleton, это может быть совершенно не очевидно. Всё становится гораздо хуже, если экземпляру класса для работы необходима определённая инициализация (например, вызов метода init(. ) или вроде того). Ещё хуже, если у вас существует несколько Singleton’ов, которые должны быть созданы и инициализированы в определённом порядке.

Загрузчик класса

Если говорить о Java, то обеспечение существования лишь одного экземпляра класса, которое так необходимо для Singleton, становится всё сложнее. Проблема в том, что классическая реализация не проверяет, существует ли один экземпляр на JVM, он лишь удостоверяется, что существует один экземпляр на classloader. Если вы пишете небольшое клиентское приложение, в котором используется лишь один classloader, то никаких проблем не возникнет. Однако если вы используете несколько загрузчиков класса или ваше приложение должно работать на сервере (где может быть запущено несколько экземпляров приложения в разных загрузчиках классов), то всё становится очень печально.

Десериализация

Ещё один интересный момент заключается в том, что на самом деле стандартная реализация Singleton не запрещает создавать новые объекты. Она запрещает создавать новые объекты через конструктор. А ведь существуют и другие способы создать экземпляр класса, и один из них — сериализация и десериализация. Полной защиты от намеренного создания второго экземпляра Singleton’а можно добиться только с помощью использования enum’а с единственным состоянием, но это — неоправданное злоупотребление возможностями языка, ведь очевидно, что enum был придуман не для этого.

Потоконебезопасность

Один из популярных вариантов реализации Singleton содержит ленивую инициализацию. Это значит, что объект класса создаётся не в самом начале, а лишь когда будет получено первое обращение к нему. Добиться этого совсем не сложно:

public static Singleton getInstance() < if (instance == null) < instance = new Singleton(); >return instance; > 

Однако здесь начинаются проблемы с потоками, которые могут создавать несколько различных объектов. Происходит это примерно так:

  • Первый поток обращается к getInstance() , когда объект ещё не создан;
  • В это время второй тоже обращается к этому методу, пока первый ещё не успел создать объект, и сам создаёт его;
  • Первый поток создаёт ещё один, второй, экземпляр класса.

Разумеется, можно просто пометить метод как synchronised , и эта проблема исчезнет. Проблема заключается в том, что, сохраняя время на старте программы, мы теперь будем терять его каждый раз при обращении к Singleton’у из-за того, что метод синхронизирован, а это очень дорого, если к экземпляру приходится часто обращаться. А ведь единственный раз, когда свойство synchronised действительно требуется — первое обращение к методу.

Есть два способа решить эту проблему. Первый — пометить как synchronised не весь метод, а только блок, где создаётся объект:

public static Singleton getInstance() < if (instance == null) < synchronized (Singleton.class) < if (instance == null) < instance = new Singleton(); >> > return instance; > 

Не забывайте, что это нельзя использовать в версии Java ниже, чем 1.5, потому что там используется иная модель памяти. Также не забудьте пометить поле instance как volatile .

Второй путь — использовать паттерн «Lazy Initialization Holder». Это решение основано на том, что вложенные классы не инициализируются до первого их использования (как раз то, что нам нужно):

public class Singleton < private Singleton() < >public static Singleton getInstance() < return SingletonHolder.instance; >private static class SingletonHolder < private static final Singleton instance = new Singleton(); >> 

Рефлексия

Мы запрещаем создавать несколько экземпляров класса, помечая конструктор приватным. Тем не менее, используя рефлексию, можно без особого труда изменить видимость конструктора с private на public прямо во время исполнения:

Class clazz = Singleton.class; Constructor constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); 

Конечно, если вы используете Singleton только в своём приложении, переживать не о чем. А вот если вы разрабатываете модуль, который затем будет использоваться в сторонних приложениях, то из-за этого могут возникнуть проблемы. Какие именно, зависит от того, что делает ваш «Одиночка» — это могут быть как и риски, связанные с безопасностью, так и просто непредсказуемое поведение модуля.

Заключение

Несмотря на то, что паттерн Singleton очень известный и популярный, у него есть множество серьёзных недостатков. Чем дальше, тем больше этих недостатков выявляется, и оригинальные паттерны из книги GOF «Design Patterns» часто сегодня считаются антипаттернами. Тем не менее, сама идея иметь лишь один объект на класс по-прежнему имеет смысл, но достаточно сложно реализовать ее правильно.

Следите за новыми постами по любимым темам

Подпишитесь на интересующие вас теги, чтобы следить за новыми постами и быть в курсе событий.

Как сериализовать Singleton?

Два десериализованных объекта singleton и singleton1 имеют разные ссылки в памяти, а должны иметь одинаковые. В класс Singleton добавь один метод, чтобы после десериализации ссылки на объекты были равны. Метод main не участвует в тестировании.

Вы не можете решать эту задачу, т.к. не залогинены.
Комментарии (198)

  • популярные
  • новые
  • старые

Для того, чтобы оставить комментарий Вы должны авторизоваться
Уровень 20
22 октября, 18:36
кто все эти добрые люди (оставившие ссылки и разъяснения ниже), делающие за JavaRush их работу ? ��
Уровень 39
19 сентября, 07:51
ссылочка по сериализации и конкретно этой задаче с синглтоном здесь хорошая инфа
Nikita Backend Developer
14 сентября, 04:47

Валидатор принял когда просто вернул ссылку на объект ourInstance. Посмотрев код чуть повнимательнее, я в первые не понимаю почему валидатор тут не более жесткий. Т.е. на мой взгляд он должен был послать меня, т.к. если я десириализовал синглотон, который ранее не был бы создан (к примеру ранее в другом main), то чтобы я получил (null?)? Думаю многие сделали сразу правильно и единственное, что должен был принять валидатор:

return getInstance();

Уровень 35
7 сентября, 03:19
нифкусна и грусна ��
Уровень 23
10 июля, 15:50
А я то уж думал, что повидал многое. ��
Anonymous #3278550 Работает в быстром темпе
29 июня, 14:13
А кто-то мне объяснит зачем попросили создать приватный метод который я не могу заюзать)))?
Уровень 23
10 июля, 15:46
RTFM: https://javarush.com/groups/posts/1410-serializacija-kak-ona-estjh-chastjh-2
21 июня, 21:58
Уровень 39
18 июня, 05:43

Сериализация и десериализация объектов singleton может быть немного сложнее, чем у обычных объектов, поскольку цель singleton состоит в том, чтобы гарантировать, что только один экземпляр класса существует во время выполнения программы. Однако, в некоторых случаях может возникнуть необходимость сериализовать и десериализовать состояние такого объекта. Вот несколько подходов, которые можно использовать. 1. Игнорировать сериализацию: Можно добавить аннотацию `transient` к полю, отвечающему за хранение экземпляра класса в объекте singleton. Это позволит игнорировать сериализацию этого поля при сохранении объекта в файл или передаче по сети. Однако при десериализации поле будет null, и вам придется восстановить экземпляр класса вручную. 2. Переопределить методы `readResolve` и `writeReplace`: В Java есть специальные методы, которые позволяют контролировать процесс сериализации и десериализации. Вы можете переопределить метод `readResolve`, чтобы при десериализации возвращать существующий экземпляр класса, а также переопределить метод `writeReplace`, чтобы при сериализации возвращать экземпляр класса, на котором вызывается `writeReplace`. Например:

private Object readResolve()  return Singleton.getInstance(); > private Object writeReplace()  return Singleton.getInstance(); >

3. Подход «enum singleton»: Вместо использования класса можно создать singleton с использованием перечисления (enum), которое по умолчанию сериализуется автоматически. Вот пример:

public enum Singleton  INSTANCE; // Добавьте в этот класс все необходимые поля и методы >

В этом случае сериализация и десериализация объекта singleton будут автоматически обработаны при использовании стандартных механизмов сериализации в Java.

Уровень 23
1 июня, 13:32

Честно говоря, немного раздражает, что при желании хоть немного разобраться всю информацию приходится искать самому, всё-таки курс денег стоит. В общем, что нужно знать, минимум: Есть два метода — readResolve() и writeReplace():

private Object writeReplace()  return Singleton.getInstance(); // не обязательно, просто пример > private Object readResolve()  return Singleton.getInstance(); // такой вариант мне кажется более правильным >

Эти методы позволяют подменить объект при его сериализации / десериализации соответственно, иногда это необходимо, как в этом примере с паттерном Singleton. Откуда вообще проблема, почему у нас создаются новые экземпляры класса при десериализации? Насколько я смог узнать — в Java существуют определённые механизмы, которые позволяют создать новый экземпляр объекта, даже если конструктор является приватным. Основная суть: при десериализации создаются новые объекты типа Singleton с такими же данными, что и исходный, но это уже другие объекты, то есть суть паттерна нарушается. Чтобы избежать этого мы определяем метод readResolve() в классе Singleton, в котором вызываем метод getInstance() — он возвращает один единственный объект ourInstance для любого числа вызовов. Таким образом, сначала программа считывает объект из потока данных (создаёт новый), но перед возвратом он подменяется на другой (наш ourInstance) Кстати, вместо вызова getInstance() можно просто возвращать ourInstance, тоже принимается 🙂

Уровень 23
1 июня, 13:39

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

Уровень 22
2 августа, 15:23
Мне кажется вот тот самый «иной подход» — private Object readResolve() < if (ourInstance == null)< ourInstance = this; >return ourInstance; >
Уровень 49
19 апреля, 12:26

Вот Изменение сериализованных данных, writeObject, readObject Чтобы изменить процесс сериализации в классе Person реализуем метод writeObject. Для модифицирования процесса десериализации определим в том же классе метод readObject. При реализации этих методов необходимо корректно восстановить данные.

private void writeObject(ObjectOutputStream stream) throws IOException  // "Криптование"/скрытие истинного значения age = age  <2; stream.defaultWriteObject(); > private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException  stream.defaultReadObject(); // "Декриптование"/восстановление истинного значения age = age >> 2; >

Показать еще комментарии

  • Курсы программирования
  • Регистрация
  • Курс Java
  • Помощь по задачам
  • Цены
  • Задачи-игры

Сообщество

JavaRush — это интерактивный онлайн-курс по изучению Java-программирования c нуля. Он содержит 1200 практических задач с проверкой решения в один клик, необходимый минимум теории по основам Java и мотивирующие фишки, которые помогут пройти курс до конца: игры, опросы, интересные проекты и статьи об эффективном обучении и карьере Java‑девелопера.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *