Java Serialization 2013
Материал из SEWiki
Что почитать
- "Effective Java", Joshua Bloch, Chapter 11
- Java Object Serialization Specification
1. Serialization.java (основная часть)
import java.io.Externalizable;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamField;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
/**
* Лекция о сериализации в Java.
*/
@SuppressWarnings({"unused"})
public class Serialization {
/****Введение****/
/**
*
* Сериализация -- запись объекта в байтовый поток.
*
* Десереализация -- CO.
*
* Все в пакете java.io
*
* Мотивация:
* 1. dump (ex. JavaBeans persistence)
* 2. RMI
*/
class Point {
double x;
double y;
}
//move to Point1.java and SimplePointTest.java
/****Процесс сериализации****/
/**
* В сериализации участвуют ВСЕ достижимые объекты.
*
* Запись:
* 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();
}
}
//move to Point5.java
/**
* Сериализация с несериализуемым предком.
*
* Если хочется отнаследовать сериализуемый класс от
* несериализуемого предка, то могут быть проблемы...
*
* Формальное требование одно -- предок должен иметь
* конструктор по-умолчанию.
*
* Но только ли?!
*/
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() -- какой объект восстановить вместо прочитанного
*
* Реализуются у разных классов!!!
*/
}
import java.io.Externalizable;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamField;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
/**
* Лекция о сериализации в Java.
*/
@SuppressWarnings({"unused"})
public class Serialization {
/****Введение****/
/**
*
* Сериализация -- запись объекта в байтовый поток.
*
* Десереализация -- CO.
*
* Все в пакете java.io
*
* Мотивация:
* 1. dump (ex. JavaBeans persistence)
* 2. RMI
*/
class Point {
double x;
double y;
}
//move to Point1.java and SimplePointTest.java
/****Процесс сериализации****/
/**
* В сериализации участвуют ВСЕ достижимые объекты.
*
* Запись:
* 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();
}
}
//move to Point5.java
/**
* Сериализация с несериализуемым предком.
*
* Если хочется отнаследовать сериализуемый класс от
* несериализуемого предка, то могут быть проблемы...
*
* Формальное требование одно -- предок должен иметь
* конструктор по-умолчанию.
*
* Но только ли?!
*/
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() -- какой объект восстановить вместо прочитанного
*
* Реализуются у разных классов!!!
*/
}