Есть книги, которые очень сильно изменят вашу жизнь. Одна из таких книг — «Эффективная Ява» Джошуа Блоха . Ниже вы можете найти небольшой эксперимент, который был вдохновлен главой 11 этой книги — «Сериализация».
Предположим, что у нас есть класс, предназначенный для наследования, который сам не является Serializable и не имеет конструктора без параметров, как в этом примере:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
public class CumbersomePoint { private String name; private double x; private double y; protected CumbersomePoint( double x, double y, String name) { this .x = x; this .y = y; this .name = name; } public String getName() { return name; } public double getX() { return x; } public double getY() { return y; } ... } |
Теперь, когда мы расширяем этот класс, например, следующим образом:
1
2
3
4
5
6
7
8
|
public class ConvenientPoint extends CumbersomePoint implements Serializable { public ConvenientPoint( double x, double y, String name) { super (x, y, name); } ... } |
и попробуем сериализовать и затем десериализовать любой из экземпляров ConvenientPoint , мы быстро столкнемся с прекрасным InvalidClassException , жалуясь на то, что нет допустимого конструктора. Ситуация выглядит немного безнадежной, пока вы не примените технику, известную как шаблон сериализации прокси .
Начнем с добавления в класс ConvenientPoint следующего внутреннего класса:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
private static class SerializationProxy implements Serializable { private String name; private double x; private double y; public SerializationProxy(ConvenientPoint point) { this .name = point.getName(); this .x = point.getX(); this .y = point.getY(); } private Object readResolve() { return new ConvenientPoint(x, y, name); } } |
Класс SerializationProxy будет представлять логическое состояние экземпляра включающего класса. Придется добавить также следующий метод в класс ConvenientPoint :
1
2
3
|
private Object writeReplace() { return new SerializationProxy( this ); } |
Теперь, когда экземпляр ConvenientPoint будет сериализован, он назначит его замену благодаря методу writeReplace — экземпляр SerializationProxy будет сериализован вместо ConvenientPoint .
С другой стороны, когда SerializationProxy будет десериализован, использование метода readResolve назначит его замену, являющуюся экземпляром ConvenientPoint .
Как видите, мы сделали сериализуемую ConvenientPoint вне зависимости от отсутствующего конструктора без параметров в не сериализуемом родительском классе.
Еще одно замечание в конце этого поста — если вы хотите защитить от взлома инвариантов класса, примененного конструктором, вы можете добавить следующий метод к классу, используя Serialization Proxy Pattern ( ConvenientPoint в нашем примере):
1
2
3
|
private void readObject(ObjectInputStream stream) throws InvalidObjectException { throw new InvalidObjectException( "Use Serialization Proxy instead." ); } |
Это предотвратит десериализацию окружающего класса.
Ссылка: | Пример шаблона сериализации прокси от нашего партнера по JCG Михала Ястака из блога «Мысли чернокнижника» . |