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

Материал из SEWiki
Перейти к: навигация, поиск
(Новая страница: «== Что почитать == * "Effective Java", Joshua Bloch, Chapter 11 * [http://docs.oracle.com/javase/7/docs/platform/serialization/spec/serialTOC.html Java O…»)
 
(Лекция о сериализации в Java.)
 
(не показано 9 промежуточных версий этого же участника)
Строка 3: Строка 3:
 
* "Effective Java", Joshua Bloch, Chapter 11
 
* "Effective Java", Joshua Bloch, Chapter 11
 
* [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]
 +
 +
== Материалы лекции ==
 +
 +
<source lang="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() -- какой объект восстановить вместо прочитанного
 +
*
 +
* Реализуются у разных классов!!!
 +
*/
 +
 +
</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() -- какой объект восстановить вместо прочитанного
 * 
 * Реализуются у разных классов!!!
 */