![]() |
Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
При необходимости создавайте метод readResolue⇐ ПредыдущаяСтр 47 из 47
В статье 2 описывается шаблон Si n gleto n и приводится следующий пример класса. синглтона. Этот класс ограничивает доступ к конструктору с тем, чтобы гарантировать создание только одного экземпляра:
public class Elvis { public static final Elvis INSTANCE = new Elvis(); private Elvis() { } // Остальное опущено }
Как отмечалось в статье 2, этот класс перестает быть синглтоном, если в его декларацию добавляются слова "implements Serializable". Не имеет значения, использует этот класс сериализованную форму, предлагаемую по умолчанию, или специальную форму (статья 55). Не имеет также значения, предоставляется ли пользователю в этом классе явный метод readObject (статья 56). Любой метод readObject, явный или применяемый по умолчанию, возвращает вновь созданный экземпляр, а не тот, что был сформирован в момент инициализации класса. До выхода версии 1.2 написать сериализуемый класс-синглтон было невозможно. В версии 1.2 механизм сериализации пополнился функцией readResolve [Serialization, 3.6]. Если класс десериализуемого объекта имеет метод readResolve с соответствующей декларацией, то по завершении десериализации этот метод будет вызван для вновь созданного объекта. Вместо ссылки на вновь созданный объект клиенту передается ссылка, возвращаемая этим методом. В большинстве случаев использования этого механизма ссылка на новый объект не сохраняется, а сам объект фактически является мертворожденным и немедленно становится объектом для сборки мусора. Если класс Elvis создан так, чтобы реализовать интерфейс Serializable, то для обеспечения свойства синглтона достаточно будет создать следующий метод readResolve:
private Object readResolve() throws ObjectStreamException { // Возвращает только истинный экземпляр Elvis и дает возможность // сборщику мусора позаботиться об Еlvis - самозванце return INSTANCE; }
217
Метод игнорирует десериализованный объект, просто возвращая уникальный экземпляр Elvis, который был создан во время инициализации класса. По этой причине необходимо, чтобы в сериализованной форме экземпляра Elvis не содержалось никаких реальных данных, а все поля экземпляра были помечены как transient. Это относится не только к классу Elvis, но и К любым другим синглтонам. Метод readResolve необходим не только для синглтонов, но и для всех остаЛЬНblХ классов, контролирующих свои экземпляры (instance-controlled), т. е. для тех классов, в которых сохранение некоего инварианта требует строгого контроля над процедурой создания экземпляров. Еще один пример класса, контролирующего свои экземпляры,- перечисление типов (статья 21), чей метод readResolve должен возвращать канонический экземпляр, соответствующий указанной константе в перечислении. Главное правило таково, что если вы пишете сериализуемый класс, не имеющий ни открытых, ни защищенных конструкторов, проверьте, нужен ли этому классу метод r~adResolve. Второй вариант применения метода readResolve - в качестве безопасной альтернаТИВbI защищенному методу readObject (статья 56). В этом случае из метода readObject изымаются все про верки параметров, а также процедуры создания резервных копий, и применяются проверки и резервное копирование, предоставляемые обычным конструктором. При использовании сериализованной формы, предлагаемой по умолчанию, метод readObject может быть полностью исключен. Как объясняется в статье 56, это дает возможность клиенту, имеющему злой умысел, создать экземпляр с испорченными инвариантами. Однако потенциально испорченный десериализованный экземпляр никогда не будет передан в активное использование. Из него просто будут извлечены данные для открытого конструктора или статического метода генерации, после чего он будет отброшен. Изящество этого подхода заключается в том, что фактически устраняются составные части сериализации, выходящие за пределы языка, что делает невозможным нарушение каких-либо инвариантов класса, имевшихся до того, как этот класс был сделан сериализуемым. Чтобы продемонстрировать эту методику, в примере с классом Реriod (статья 56) вместо защищенного метода readObject можно воспользоваться следующим методом readResolve:
// Идиома защищенного метода readResolve private Object readResolve() throws ObjectStreamException { return new Period(start, end); }
Этот метод останавливает обе атаки, описанные в статье 56. Идиома защищенного метода readResolve имеет несколько преимуществ перед защищенным readObject. Это чисто механический прием, позволяющий делать класс сериализуемым, не подвергая риску его инварианты. Он требует небольшого объема кода, немного размышлений и работает надежно. Наконец, он устраняет искусственные ограничения, накладываемые сериализацией на использование окончательных полей.
218
Хотя идиома защищенного метода readResolve не имеет широкого применения, она заслуживает серьезного рассмотрения. Главный ее недостаток состоит в том, что она не годится для класса, который можно наследовать за пределами соответствующего пакета. Это не касается неизменяемых классов, поскольку они обычно являются окончательными (статья 13). Недостатком этой идиомы является и некоторое снижение скорости десериализации, поскольку она сопровождается созданием дополнительного объекта. На моей машине десериализация экземпляров Реriod замедлил ась примерно на один процент по сравнению с защищенным методом readObj ect. Большое значение имеет доступность метода readResolve. Если вы помещаете его в окончательный класс, например синглтон, он должен быть закрытым. Помещая метод readResolve в расширяемый класс, вы должны внимательно изучить его доступность. Если он будет закрытым, его не удастся использовать ни в одном из подклассов. Если он будет доступен только в пакете, его применение ограничится подклассами из того же пакета. Защищенный или открытый метод будет доступен во всех подклассах, если только они его не переопределят. Если метод readResolve является защищенным или открытым и не переопределяется в подклассе, то десериализация сериализованного экземпляра подкласса создаст экземпляр суперкласса, а это, вероятно, будет совсем не то, чего вы ожидали. Предыдущее рассуждение наводит на мысль о причине, по которой в классах, допускающих наследование, метод readResolve не может служить заменой защищенному методу readObject. Если бы в суперклассе метод readResolve был помечен как final, это не позволило бы правильно десериализовать экземпляры подкласса. Если бы он был переопределяемым, то подкласс, написанный злоумышленником, мог бы его переопределить таким методом, который возвращает испорченный экземпляр. Подведем итоги. Вы должны использовать метод readResolve для защиты "инвариантов контроля над экземплярами" в синглтонах и других классах, которые контролируют свои экземпляры. По существу, метод readResolve превращает метод readObject из де-факто открытого конструктора в де-факто открытый статический метод генерации. Метод readResolve также полезен в качестве простой альтернативы защищенному методу readObject в классах, для которых запрещено наследование за пределами соответствующего пакета.
219 |
Последнее изменение этой страницы: 2019-04-11; Просмотров: 210; Нарушение авторского права страницы