Есть книги, которые очень сильно изменят вашу жизнь. Одна из таких книг является
«Эффективное 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."); }
Это предотвратит десериализацию окружающего класса.