Java Serialization 2013
Материал из SEWiki
Что почитать
- "Effective Java", Joshua Bloch, Chapter 11
- Java Object Serialization Specification
Лекция о сериализации в Java.
/****Введение****/
/**
*
* Сериализация -- запись объекта в байтовый поток.
*
* Десереализация -- CO.
*
* Все в пакете java.io
*
* Мотивация:
* 1. dump (ex. JavaBeans persistence)
* 2. RMI
*/
class Point {
double x;
double y;
}
/**
* В простейшем случае достаточно добавить маркерный интерфейс implements
* Serializable
*/
@SuppressWarnings("serial")
class Point1 implements Serializable {
double x;
double y;
Point1(double x, double y) {
this.x = x;
this.y = y;
}
}
class SimplePointTest {
void SimpleWrite(OutputStream os) throws /*NotSerializableException,*/ IOException {
Point1 p = new Point1(10., 10.);
ObjectOutput out = new ObjectOutputStream(os);
out.writeObject(p);
os.flush();
}
void SimpleRead(InputStream is) throws IOException, ClassNotFoundException {
ObjectInput in = new ObjectInputStream(is);
Point1 p = (Point1) in.readObject();
System.out.println("x=" + p.x);
System.out.println("y=" + p.y);
}
@Test
public void SimpleTest() throws FileNotFoundException, IOException,
ClassNotFoundException {
try (OutputStream os = new FileOutputStream("point.ser")) {
SimpleWrite(os);
}
try (InputStream is = new FileInputStream("point.ser")) {
SimpleRead(is);
}
}
}
/****Процесс сериализации****/
/**
* В сериализации участвуют ВСЕ достижимые объекты.
*
* Запись:
* 1. Запись предка (если он сериализуем)
* 2. Запись полей (если они сериализуемы)
*
* Циклические зависимости корректно обрабатываются автоматически.
*
* Чтение:
* 1. Выделение памяти под объект
* 2. Чтение предка
* 3. Чтение полей
*/
/**
* Все поля должны быть сериализуемы!
*/
@SuppressWarnings("serial")
class Polygon implements Serializable {
List<Point3> vertices = new ArrayList<>();
}
/**
* Пока все просто, но впечатление обманчиво!
*
* 1. Внутреннее представление становится частью интерфейса
* Инкапсуляция летит к чертям!
* 2. Увеличивает объемы тестирования новых версий
* 3. Дополнительный конструктор!
* Необходимо обеспечивать все инварианты
* Некоторые дыры в безопасности
* 4. Все потомки становятся сериализуемыми!
*/
/****Управление сериализацией****/
/**
* Serial version UID (stream unique identifier)
*
* Применяется для обеспечения совместимости, когда версии класса изменяются
*
* По-умолчанию автоматически создается на основе имени, интерфейсов,
* public и protected полей
*
* В случае несовпадения -- InvalidClassException
*
*/
class Point2 implements Serializable {
private static final long serialVersionUID = 4889340678034881968L;
double x;
double y;
}
/**
* Модификатор transient.
* Исключает поле из процесса сериализации.
* Примеры:
* 1. Избыточная информация (предподсчитанные значения)
* 2. Несериализуемые поля
*
*/
class Point3 implements Serializable {
private static final long serialVersionUID = 4889340678034881968L;
double x;
double y;
transient int precomputedHash;
public int hashCode() {
return precomputedHash;
}
private int countHash() {
//some very involved procedure
return 0;
}
Point3(double x, double y) {
this.x = x;
this.y = y;
precomputedHash = countHash();
}
}
/**
* Настраиваемая сериализация
* Методы
* readObject(ObjectInputStream in) -- должен прочесть состояние из потока
* writeObject(ObjectOutputStream out) -- должен записать состояние в поток
*
* Процесс чтения
* 1. Выделение памяти
* 2. Вызов readObject
*
*/
class Point4 implements Serializable {
private static final long serialVersionUID = 4889340678034881968L;
double x;
double y;
transient int precomputedHash;
public int hashCode() {
return precomputedHash;
}
private int countHash() {
//some very involved procedure
return 0;
}
Point4(double x, double y) {
this.x = x;
this.y = y;
precomputedHash = countHash();
}
/*
* Отвечает только за запись полей ЭТОГО класса, не предка и не потомка!
*/
private void writeObject(ObjectOutputStream s) throws IOException {
/*
* Реализует стандартный механизм сериализации.
* Может вызываться только из writeObject, иначе NotActiveException
*/
s.defaultWriteObject();
}
//отвечает только за чтение полей ЭТОГО класса, не предка и не потомка!
private void readObject(ObjectInputStream s) throws IOException,
ClassNotFoundException {
/*
* Реализует стандартный механизм десериализации.
* Может вызываться только из readObject, иначе NotActiveException
*/
s.defaultReadObject();
//восстановление инвариантов
precomputedHash = countHash();
}
}
/**
* Что если внутреннее представление класса изменилось?
*
* Ручная сериализация полей
*
* Константа ObjectStreamField[] serialPersistentFields -- сериализуемые поля
* ObjectOutputStream.PutField putFields() -- поля для записи
*
* writeFields записывает поля
*
* ObjectInputStream.GetField readFields() -- чтение полей
*
*/
class Point5 implements Serializable {
private static final long serialVersionUID = 4889340678034881968L;
double angle;
double rad;
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("x", double.class),
new ObjectStreamField("y", double.class) };
private void writeObject(ObjectOutputStream s) throws IOException {
ObjectOutputStream.PutField fields = s.putFields();
fields.put("x", Math.cos(angle) * rad);
fields.put("y", Math.sin(angle) * rad);
s.writeFields();
}
private void readObject(ObjectInputStream s) throws IOException,
ClassNotFoundException {
ObjectInputStream.GetField fields = s.readFields();
double x = (double) fields.get("x", 0.);
double y = (double) fields.get("y", 0.);
// count angle and mod here
// angle = ...
// rad = ...
}
}
/**
* Сериализация с несериализуемым предком.
*
* Если хочется отнаследовать сериализуемый класс от
* несериализуемого предка, то могут быть проблемы...
*
* Формальное требование одно -- предок должен иметь
* конструктор по-умолчанию.
*
* Но только ли?!
*/
class NonSerializablePoint {
private double x;
private double y;
protected double getX() {return x;}
protected double getY() {return y;}
protected void init(double x, double y) {
this.x = x;
this.y = y;
}
protected NonSerializablePoint() {}
public NonSerializablePoint(double x, double y) {
init(x, y);
}
}
// Serializable subclass of nonserializable stateful class
class ColoredPoint extends NonSerializablePoint implements Serializable {
private int color;
// Конструктор использует обычный механизм
public ColoredPoint(double x, double y, int color) {
super(x, y);
this.color = color;
}
private void readObject(ObjectInputStream s) throws IOException,
ClassNotFoundException {
s.defaultReadObject();
// Manually deserialize and initialize superclass state
double x = s.readDouble();
double y = s.readDouble();
init(x, y);
}
private void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
// Manually serialize superclass state
s.writeDouble(getX());
s.writeDouble(getY());
}
private static final long serialVersionUID = 1856835860954L;
}
/**
* Externalizable
*
* Сериализация "в ручную"
*
* void readExternal(ObjectInput in)
* void writeExternal(ObjectOutput out)
*
* Должен быть конструктор по-умолчанию
*
* Чтение: вызов конструктора по-умолчанию, вызов readExternal
*/
class Point6 implements Externalizable {
private double x;
private double y;
Point6(double x, double y) {
this.x = x;
this.y = y;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeDouble(x);
out.writeDouble(y);
}
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
this.x = in.readDouble();
this.y = in.readDouble();
}
}
/**
* "Подмена" сериализуемого объекта.
*
* Когда вместо самого объекта сериализуется его альтернативное представление
* в виде другого объекта некоторого (возможно того же самого) сериализуемого класса.
*
* Object writeReplace() -- какой объект записать вместо данного
* Object readResolve() -- какой объект восстановить вместо прочитанного
*
* Реализуются у разных классов!!!
*/