Есть книги, которые очень сильно изменят вашу жизнь. Одна из таких книг является
«Эффективное Java» от
Джошуа Блох . Ниже вы можете найти небольшой эксперимент, который был вдохновлен главой 11 этой книги — «Сериализация».
Предположим, что у нас есть класс, предназначенный для наследования, который сам не является
Serializable и не имеет конструктора без параметров, как в этом примере:
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;
}
...
}
Теперь, когда мы расширяем этот класс, например, следующим образом:
public class ConvenientPoint extends CumbersomePoint implements Serializable {
public ConvenientPoint(double x, double y, String name) {
super(x, y, name);
}
...
}
и попытаться сериализовать и затем десериализовать любой из
экземпляров ConvenientPoint , мы быстро столкнемся с прекрасным
InvalidClassException , жалуясь на то, что нет допустимого конструктора. Ситуация выглядит своего рода безнадежным, пока вы не применять технику , известную как
Сериализация Proxy Pattern .
Начнем с добавления в
класс ConvenientPoint следующего внутреннего класса:
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 :
private Object writeReplace() {
return new SerializationProxy(this);
}
Теперь, когда
экземпляр ConvenientPoint будет сериализован, он назначит его замену благодаря
методу writeReplace —
экземпляр SerializationProxy будет сериализован вместо
ConvenientPoint .
С другой стороны, когда
SerializationProxy будет десериализован,
использование метода readResolve назначит его замену, являющуюся
экземпляром ConvenientPoint .
Как видите, мы сделали
сериализуемую ConvenientPoint вне зависимости от отсутствующего конструктора без параметров в не сериализуемом родительском классе.
Еще одно замечание в конце этого поста — если вы хотите защитить от взлома инвариантов класса, примененного конструктором, вы можете добавить следующий метод к классу, используя
Serialization Proxy Pattern (
ConvenientPoint в нашем примере):
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
throw new InvalidObjectException("Use Serialization Proxy instead.");
}
Это предотвратит десериализацию окружающего класса.