Java Serialization 2013 — различия между версиями

Материал из SEWiki
Перейти к: навигация, поиск
(1. Serialization.java (основная часть))
(Лекция о сериализации в Java.)
 
(не показаны 2 промежуточные версии этого же участника)
Строка 4: Строка 4:
 
* [http://docs.oracle.com/javase/7/docs/platform/serialization/spec/serialTOC.html Java Object Serialization Specification]
 
* [http://docs.oracle.com/javase/7/docs/platform/serialization/spec/serialTOC.html Java Object Serialization Specification]
  
== 1. Serialization.java ==
+
== Материалы лекции ==
  
 
<source lang="java">
 
<source lang="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;
+
  
 +
/**
 +
*
 +
* Сериализация -- запись объекта в байтовый поток.
 +
*
 +
* Десереализация -- CO.
 +
*
 +
* Все в пакете java.io
 +
*
 +
* Мотивация:
 +
*  1. dump (ex. JavaBeans persistence)
 +
*  2. RMI
 +
*/
 +
class Point {
 +
    double x;
 +
    double y;
 +
}
  
  
 
/**
 
/**
  * Лекция о сериализации в Java.
+
  * В простейшем случае достаточно добавить маркерный интерфейс implements
 +
* Serializable
 
  */
 
  */
@SuppressWarnings({"unused"})
+
@SuppressWarnings("serial")
public class Serialization {
+
class Point1 implements Serializable {
 +
    double x;
 +
    double y;
  
/****Введение****/
+
    Point1(double x, double y) {
+
        this.x = x;
/**
+
        this.y = y;
*
+
    }
* Сериализация -- запись объекта в байтовый поток.
+
*
+
* Десереализация -- 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<>();
+
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();
* Пока все просто, но впечатление обманчиво!
+
    }
*  
+
* 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;
+
    void SimpleRead(InputStream is) throws IOException, ClassNotFoundException {
double y;
+
        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")) {
* Модификатор transient.
+
            SimpleRead(is);
* Исключает поле из процесса сериализации.
+
        }
* Примеры:
+
    }
* 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;
+
* Запись:
+
*  1. Запись предка (если он сериализуем)
public int hashCode() {
+
*  2. Запись полей (если они сериализуемы)
return precomputedHash;
+
*
}
+
* Циклические зависимости корректно обрабатываются автоматически.
+
*  
private int countHash() {
+
* Чтение:
//some very involved procedure
+
* 1. Выделение памяти под объект
return 0;
+
* 2. Чтение предка
}
+
* 3. Чтение полей
+
*/
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 {
+
*/
/*
+
@SuppressWarnings("serial")
* Реализует стандартный механизм десериализации.
+
class Polygon implements Serializable {
* Может вызываться только из readObject, иначе NotActiveException
+
*/
+
s.defaultReadObject();
+
//восстановление инвариантов
+
precomputedHash = countHash();
+
}
+
}
+
  
 +
    List<Point3> vertices = new ArrayList<>();
 +
   
 +
}
  
 
 
 
//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;
+
* 1. Внутреннее представление становится частью интерфейса
}
+
*      Инкапсуляция летит к чертям!
+
* 2. Увеличивает объемы тестирования новых версий
private void readObject(ObjectInputStream s) throws IOException,
+
* 3. Дополнительный конструктор!
ClassNotFoundException {
+
*      Необходимо обеспечивать все инварианты
s.defaultReadObject();
+
*      Некоторые дыры в безопасности
// Manually deserialize and initialize superclass state
+
* 4. Все потомки становятся сериализуемыми!
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;
+
/****Управление сериализацией****/
}
+
  
+
/**
+
* Serial version UID (stream unique identifier)
/**
+
*  
* Externalizable
+
* Применяется для обеспечения совместимости, когда версии класса изменяются
*  
+
  *
* Сериализация "в ручную"
+
* По-умолчанию автоматически создается на основе имени, интерфейсов,
*  
+
* public и protected полей
* void readExternal(ObjectInput in)
+
*  
* void writeExternal(ObjectOutput out)
+
* В случае несовпадения -- InvalidClassException
*  
+
*  
* Должен быть конструктор по-умолчанию
+
*/
*  
+
class Point2 implements Serializable {
* Чтение: вызов конструктора по-умолчанию, вызов readExternal
+
    private static final long serialVersionUID = 4889340678034881968L;
*/
+
class Point6 implements Externalizable {
+
private double x;
+
private double y;
+
  
Point6(double x, double y) {
+
    double x;
this.x = x;
+
    double y;
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.Serializable;
 
  
 
/**
 
/**
  * В простейшем случае достаточно добавить маркерный интерфейс implements
+
  * Модификатор transient.
  * Serializable
+
* Исключает поле из процесса сериализации.
 +
* Примеры:
 +
*  1. Избыточная информация (предподсчитанные значения)
 +
*  2. Несериализуемые поля
 +
  *  
 
  */
 
  */
@SuppressWarnings({ "serial"})
+
class Point3 implements Serializable {
public class Point1 implements Serializable {
+
    private static final long serialVersionUID = 4889340678034881968L;
double x;
+
double y;
+
 
+
Point1(double x, double y) {
+
this.x = x;
+
this.y = y;
+
}
+
  
 +
    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();
 +
    }
 
}
 
}
  
import java.io.FileInputStream;
+
/**
import java.io.FileNotFoundException;
+
* Настраиваемая сериализация
import java.io.FileOutputStream;
+
* Методы
import java.io.IOException;
+
*  readObject(ObjectInputStream in) -- должен прочесть состояние из потока
import java.io.InputStream;
+
*  writeObject(ObjectOutputStream out) -- должен записать состояние в поток
import java.io.ObjectInput;
+
*
import java.io.ObjectInputStream;
+
* Процесс чтения
import java.io.ObjectOutput;
+
* 1. Выделение памяти
import java.io.ObjectOutputStream;
+
*  2. Вызов readObject
import java.io.OutputStream;
+
 
+
*/
import org.junit.Test;
+
class Point4 implements Serializable {
 
+
    private static final long serialVersionUID = 4889340678034881968L;
@SuppressWarnings("javadoc")
+
public 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
+
    double x;
public void SimpleTest() throws FileNotFoundException, IOException,
+
    double y;
ClassNotFoundException {
+
   
try (OutputStream os = new FileOutputStream("point.ser")) {
+
    transient int precomputedHash;
SimpleWrite(os);
+
   
}
+
    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();
 +
    }
  
try (InputStream is = new FileInputStream("point.ser")) {
+
    //отвечает только за чтение полей ЭТОГО класса, не предка и не потомка!
SimpleRead(is);
+
    private void readObject(ObjectInputStream s) throws IOException,
}
+
            ClassNotFoundException {
}
+
        /*
 +
        * Реализует стандартный механизм десериализации.
 +
        * Может вызываться только из readObject, иначе NotActiveException
 +
        */
 +
        s.defaultReadObject();
 +
        //восстановление инвариантов
 +
        precomputedHash = countHash();
 +
    }
 
}
 
}
 
import java.io.IOException;
 
import java.io.ObjectInputStream;
 
import java.io.ObjectOutputStream;
 
import java.io.ObjectStreamField;
 
import java.io.Serializable;
 
  
 
/**
 
/**
Строка 408: Строка 234:
 
  *  
 
  *  
 
  */
 
  */
@SuppressWarnings("unused")
+
class Point5 implements Serializable {
public class Point5 implements Serializable {
+
    private static final long serialVersionUID = 4889340678034881968L;
private static final long serialVersionUID = 4889340678034881968L;
+
  
double angle;
+
    double angle;
double rad;
+
    double rad;
  
private static final ObjectStreamField[] serialPersistentFields = {
+
    private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("x", double.class),
+
            new ObjectStreamField("x", double.class),
new ObjectStreamField("y", double.class) };
+
            new ObjectStreamField("y", double.class) };
  
private void writeObject(ObjectOutputStream s) throws IOException {
+
    private void writeObject(ObjectOutputStream s) throws IOException {
ObjectOutputStream.PutField fields = s.putFields();
+
        ObjectOutputStream.PutField fields = s.putFields();
fields.put("x", Math.cos(angle) * rad);
+
        fields.put("x", Math.cos(angle) * rad);
fields.put("y", Math.sin(angle) * rad);
+
        fields.put("y", Math.sin(angle) * rad);
s.writeFields();
+
        s.writeFields();
}
+
    }
  
private void readObject(ObjectInputStream s) throws IOException,
+
    private void readObject(ObjectInputStream s) throws IOException,
ClassNotFoundException {
+
            ClassNotFoundException {
ObjectInputStream.GetField fields = s.readFields();
+
        ObjectInputStream.GetField fields = s.readFields();
double x = (double) fields.get("x", 0.);
+
        double x = (double) fields.get("x", 0.);
double y = (double) fields.get("y", 0.);
+
        double y = (double) fields.get("y", 0.);
// count angle and mod here
+
        // count angle and mod here
// angle = ...
+
        // angle = ...
// rad = ...
+
        // 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() -- какой объект восстановить вместо прочитанного
 +
*
 +
* Реализуются у разных классов!!!
 +
*/
  
 
</source>
 
</source>

Текущая версия на 16:21, 22 мая 2013

Что почитать

Материалы лекции

/****Введение****/

/**
 * 
 * Сериализация -- запись объекта в байтовый поток.
 * 
 * Десереализация -- 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() -- какой объект восстановить вместо прочитанного
 * 
 * Реализуются у разных классов!!!
 */