Java Serialization 2013

Материал из SEWiki
Версия от 16:20, 22 мая 2013; Snurk (обсуждение | вклад) (Лекция о сериализации в Java.)

Перейти к: навигация, поиск

Что почитать

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