Глава 11 • Сериализация
только в рамках пакета, его действие будет распространяться только на подклассы внутри одного и того же пакета. Если он защищенный или открытый, то его действие будет распространяться на все подклассы, не переопределяющие его. Если метод readResolve защищен или явля ется открытым и подклассы его не переопределяют, то десериализация и сериализация экземпляра подкласса произведет экземпляр суперклас са, который, скорее всего, приведет к ошибке ClassCastException.
Подведем итоги. Вам нужно использовать перечислимые типы, чтобы установить контроль экземпляров над инвариантами, насколь ко это возможно. Если это невозможно и вам нужен класс, который был бы и сериализуемым, и с контролем экземпляров, то вам нужен метод read Resolve, чтобы убедиться, что все поля экземпляров клас са являются либо примитивными, либо переходными (transient).
Рассмотрите использование агентов сериализации вместо сериализованных экземпляров
Как уже говорилось в статье 74 и обсуждалось на протяжении всей главы, применение Serializable увеличивает вероятность ошибок и проблем с безопасностью, так как он приводит к созданию экземпля ров, использующих механизмы, за пределами языка вместо обычных конструкторов. Есть тем не менее прием, который существенно снижает подобный риск. Этот прием известен как шаблон агента сериализации.
Шаблон агента сериализации довольно прямолинеен. Во-пер вых, создайте закрытый статический вложенный класс сериализуе мого класса, который точно представляет собой логическое состояние экземпляра окружающего класса. Этот вложенный класс, известный как агент сериализации, должен иметь один конструктор, типом па раметров которого является окружающий класс. Этот конструктор просто копирует данные из его аргумента: ему не требуется провер ка целостности или резервное копирование. С точки зрения дизайна сериализованная форма агента сериализации по умолчанию является
С татья 78
совершенной сериализованной формой окружающего класса. И окру жающий класс, и его агент сериализации должны быть декларирова ны так, чтобы реализовывать Serializable.
Например, рассмотрим неизменяемый класс Period, написанный в статье 39 и сериализованный в статье 76. Вот агент сериализации для этого класса. Period настолько прост, что его агент сериализации содержит в точности те же самые поля, что и класс:
// Агент сериализации класса Period
private static class SerializationProxy implements Serializable { private final Date start;
private final Date end; SerializationProxy(Period p) {
this.start = p.start; this.end = p.end;
}
private static final long serialVersionlllD = 234098243823485285L; // Any number will do (Item 75)
}
Затем добавьте следующий метод writeReplace к окружающе му классу. Этот метод можно дословно скопировать в любой класс
сагентом сериализации:
//writeReplace method for the serialization proxy pattern private Object writeReplace() {
return new SerializationProxy(this);
}
Присутствие этого метода заставляет систему сериализации выдать экземпляр SerializzationProxy вместо экземпляра окру жающего класса. Другими словами, метод writeReplace переводит экземпляр окружающего класса в его агент сериализации до самой сериализации.
При использовании метода writeReplace система сериализации никогда не выдаст сериализованный экземпляр окружающего клас са, но его можно сфабриковать при попытке злонамеренно разрушить
Глава 11 • Сериализация
инварианты класса. Для того чтобы гарантировать, что такая атака не удастся, просто добавьте метод readObject к окружающему классу:
// Метод readObject для шаблона агента сериализации private void readObject(ObjectInputStream stream) throws InvalidObjectException {
throw new InvalidObjectException(“Proxy required”);
}
Наконец, предоставьте метод readResolve классу SerializationProxy, который возвратит логически эквивалентный экземпляр окру жающего класса. Присутствие этого метода приводит к тому, что система сериализации переводит агента сериализации обратно в эк земпляр окружающего класса при десериализации.
Этот метод readResolve создает экземпляр окружающего класса, используя только открытый API, и в этом и заключается вся красота шаблона. Он в большей степени избегает неязыковой сути сериали зации, потому что создается десериализованный экземпляр с исполь зованием тех же самых конструкторов, методов статической генера ции, как при создании любого другого экземпляра. Это освобождает вас от необходимости отдельно проверять, чтобы десериализованные экземпляры подчинялись инвариантам класса. Если методы стати ческой генерации класса или конструкторы устанавливают эти ин варианты и методы его экземпляра поддерживают их, то у вас есть гарантия, что сериализация будет также их поддерживать.
Вот пример метода readResolve для вышеупомянутого Period.
SerializationProxy
// Метод readResolve для Period.SerializationProxy private Object readResolve() {
return new Period(start, end); // Uses public constructor
}
Как и подход с использованием резервного копирования (ста тья 76), подход с использованием агента сериализации препятствует тяжелым бит-потоковым атакам (статья 76) и атакам кражи вну
С татья 78
треннего поля (статья 76). В отличие от двух предыдущих подходов этот позволяет полю, принадлежащему к Period, быть завершенным, что требуется для того, чтобы класс Period был на самом деле не изменяемым (статья 15). И в отличие от двух предыдущих подхо дов он не требует длительного размышления. Вам не нужно думать о том, какие поля могут быть дискредитированы при атаках сериали зации, вам также не нужно явно выполнять проверку действительно сти в качестве части десериализации.
Рассмотрим случай с EnumSet (статья 32). У этого класса нет от крытого конструктора, только методы статической генерации. С точ ки зрения клиента, она возвращает экземпляры EnumSet, но на самом деле она возвращают один или два подкласса, в зависимости от раз мера основного перечислимого типа (статья 1). Если у основного пе речислимого типа 64 или менее элементов, то методы статической генерации выдают Regula гEnumSet; в противном случае они возвра щают JumboEnumSet. Теперь посмотрим, что происходит, если вы се риализуете набор перечислимых типов, в котором перечислимый тип содержит 60 элементов, затем добавите к нему еще пять элемен тов и десериализуете набор. При сериализации это был экземпляр RegularEnumSet, но лучше бы ему быть JumboEnumSet при десериали зации. На самом деле это точно то, что и происходит, потому что EnumSet использует шаблон агента сериализации. Если вам интерес но, вот как выглядит агент сериализации EnumSet. Он действительно настолько прост:
// Агент сериализации EnumSet
private static class SerializationProxy <E extends Enum<E>> implements Serializable {
//Тип элементов данного набора перечислимых типов private final Class<E> elementType;
//Элементы, содержащиеся в данном наборе перечислимых типов private final Enum[] elements;
SerializationProxy(EnumSet<E> set) { elementType = set.elementType;
elements = set.toArray(EMPTY_ENUM_ARRAY); // (Item 43)
Глава 11 • Сериализация
}
private Object readResolve() {
EnumSet<E> result = EnumSet.noneOf(elementType); for (Enum e : elements)
result.add((E)e); return result;
}
private static final long serialVersionUID = 362491234563181265L;
}
У шаблона агента сериализации есть два ограничения. Он несо вместим с классами, расширяемыми своими клиентами (статья 17). Он также несовместим с некоторыми классами, диаграммы объек тов которых содержат цикличности: если вы попытаетесь запустить метод на объекте в рамках метода readResolve агента сериализации, то получите ошибку ClassCastException, так как у вас еще нет объек та, а только его агент сериализации.
Наконец, дополнительные возможности и безопасность шаблона агента сериализации не свободны. Но моей машине на 14% более затратно сериализовывать и десериализовывать экземпляры Period с помощью агента сериализации, чем с помощью резервного копиро-
ВЭ.НИЯ.
Подведем итоги. Рассмотрите шаблон агента сериализации каж дый раз, когда вам приходится писать методы readObject или writeObject на классе, который нерасширяем для своих клиентов. Этот ша блон, возможно, является простейшим путем сериализации объектов с нетривиальными инвариантами.
Список литературы
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[ A m o ld 0 5 ] |
A rn o ld , |
K e n , |
Ja m e s |
G o slin g , |
an d |
D a v id H o lm e s. The Java™ |
|
Programming |
Language, |
Fourth |
Edition. A d d iso n - W e sle y , |
|
B o sto n , |
|
2 0 0 5 . |
I S B N : |
0 3 2 1 3 4 9 8 0 6 . |
|
|
|
|
[A sse r ts] |
Programming |
with |
Assertions. |
S u n |
M icro sy ste m s. |
|
2 0 0 2 . |
|
< h t t p : / / ja v a .s u n .c o m / ja v a s e / 6 / d o c s / te c h n o te s / g u id e s / |
|
la n g u a g e /a sse rt.h tm l> |
|
|
|
|
|
|
|
|
|
[ B e c k 9 9 ] |
B e ck , |
K en t. |
Extreme |
Programming |
Explained: Embrace |
|
Change. |
A d d iso n -W e sle y , |
|
R e a d in g , |
M A , |
1 9 9 9 . |
I S B N : |
|
0 2 0 1 6 1 6 4 1 6 . |
|
|
|
|
|
|
|
|
|
|
|
|
[B e c k 0 4 ] |
B e ck , |
K e n t. |
JUnit |
Pocket |
Guide. O ’ R eilly |
M e d ia , |
In c., |
|
S e b a sto p o l, |
C A , |
2 0 0 4 . I S B N : |
0 5 9 6 0 0 7 4 3 4 . |
|
|
|
[B lo c h O l] |
B lo ch , Jo sh u a . Effective Java™ Programming Language Guide. |
|
A d d iso n -W e sle y , |
B o sto n , |
2 0 0 1 . I S B N : |
0 2 0 1 3 1 0 0 5 8 . |
|
|
[B lo c h 0 5 ] |
B lo ch , Jo sh u a, an d N e a l G after. Java™ Puzzlers: Traps, Pitfalls, |
|
and Corner |
Cases. A d d iso n -W e sle y , |
B o sto n , |
2 0 0 5 . |
I S B N : |
|
0 3 2 1 3 3 6 7 8 X . |
|
|
|
|
|
|
|
|
|
|
|
|
[B lo c h 0 6 ] |
B lo ch , |
Jo sh u a . |
C ollection s. |
In |
The Java™ Tutorial: A |
|
Short |
|
Course on the Basics, Fourth Edition. S h aro n |
Z a k h o u r |
et al. |
|
A d d iso n -W e sle y , |
B o sto n , 2 0 0 6 . |
I S B N : 0 3 2 1 3 3 4 2 0 5 . |
|
P a g e s |
|
2 9 3 —3 6 8 . |
A lso |
available |
|
as |
< h t t p :/ / ja v a .s u n .c o m / d o c s / |
b oo k s / tutorial / collections / in dex .h tm l> .
Список литературы
[Java6-feat] |
Java™ SE 6 Release Notes: Features and Enhancements. Sun |
|
Microsystems. 2008. |
|
<http: / / java.sun.com / javase/ 6 / webnotes/ features.html> |
[JavaBeans] |
J avaBeans™ Spec. Sun Microsystems. March 2001. |
|
<http: / /java.sun.com/products/javabeans/docs/spec.html> |
[Javadoc-5.0] |
What's New in Javadoc 5.0. Sun Microsystems. 2004. |
|
< http: / /java.sun .com /j2se /1 .5 .0 /d o cs / guide/javadoc/ |
|
whatsnew-1.5.0.html> |
[Javadoc-guide] |
How to Write Doc Comments for the Javadoc Tool. Sun |
|
Microsystems. 2000—2004. |
|
<http: / /java.sun.com/j2se/javadoc/writingdoccomments/ |
|
index.html> |
[Javadoc-ref] Javadoc Reference Guide. Sun Microsystems. 2002—2006.
<http:/ /java.sun.com /javase / 6 / docs / technotes/tools / solaris/javadoc.html>
<http: / /java.su n .com /javase/6 / d ocs/tech n otes/tools/ windows/ javadoc.html>
[JavaSE6] |
Java™ Platform, Standard Edition 6 A PI Specification. Sun |
|
Microsystems. March 2006. |
|
|
<http: / / java.sun.com/ javase/ 6 / docs/api / > |
[J^] |
Gosling, James, Bill Joy, and Guy Steele, and Gilad Bracha. |
|
The Java™ Language Specification, Third Edition. Addison- |
|
Wesley, Boston, 2005. ISBN: 0321246780. |
[Kahan91] |
Kahan, William, andJ. W. Thomas.Augmenting a Programming |
|
Language with Complex Arithmetic. |
U C B /C SD -91 -667, |
|
University of California, Berkeley, 1991. |
|
[Knuth74] |
Knuth, Donald. Structured Programming with go to Statements. |
|
In Computing Surveys 6 (1974): 261—301. |
[Langer08] |
Langer, Angelika. Java Generics FAQs |
— Frequently Asked |
|
Questions. 2008. |
|
Список литературы
|
<http: / / www.angelikalanger.com/ GenericsFAQ/JavaGenerics |
|
FAQ.html> |
|
[LeaOO] |
Lea, Doug. Concurrent Programming in Java™: Design |
|
Principles and Patterns, Second Edition, Addison-Wesley, |
|
Boston, 2000. ISBN : 0201310090. |
|
[Lieberman86] |
Lieberman, Henry. Using Prototypical Objects to Implement |
|
Shared Behavior in Object-Oriented Systems. In Proceedings of |
|
the First ACM Conference on Object-Oriented Programming |
|
Systems, Languages, and Applications, pages 214—223, |
|
Portland, September 1986. ACM Press. |
|
[Liskov87] |
Liskov, B. Data Abstraction and Hierarchy. In Addendum to |
|
the Proceedings of OOPSLA ‘87 and SICPLAN Notices, Vol. |
|
23, No. 3 :1 7 -3 4 , May 1988. |
|
[Meyers98] |
Meyers, Scott. Effective C + + , Second Edition: 50 Specific |
|
Ways to Improve Your Programs and Designs. Addison-Wesley, |
|
Reading, MA, 1998. ISBN: 0201924889. |
|
[Naftalin07] |
Naftalin, Maurice, and Philip Wadler. Java Generics and |
|
Collections. O ’Reilly Media, Inc., Sebastopol, CA, 2007. |
|
ISBN: 0596527756. |
|
[Parnas72] |
Parnas, D. L. On the Criteria to Be Used in Decomposing |
|
Systems into Modules. In Communications |
of the ACM 15 |
|
(1972): 1053-1058. |
|
[Posix] |
9945-1:1996 (ISO /IE C ) [IE E E /A N SI |
Std. 1003.1 1995 |
|
Edition] Information Technology— Portable Operating System |
|
Interface (P O S IX )— Part 1: System Application: Program |
|
Interface (A PI) C Language] (A N SI), IE E E Standards Press, |
|
ISBN: 1559375736. |
|
[PughOl] |
The "Double-Checked Locking is Broken” Declaration. Ed. |
|
William Pugh. University of Maryland. March 2001. |
<http: / / www.cs.umd.edu / ~pugh / java/m em ory Model / DoubleCheckedLocking.html>