Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: Общие вопросы > Вопрос по сериализации


Автор: Artiom18 10.12.2013, 18:59
Возник такой вопрос.
Есть класс
Код

public class Line implements java.io.Serializable {
    private Point point1;
    private Point point2;
    private int index;

    public Line() {
        System.out.println("Constructing empty line");
    }

    Line(Point p1, Point p2, int index) {
        System.out.println("Constructing line: " + index);
        this.point1 = p1;
        this.point2 = p2;
        this.index = index;
    }

    public int getIndex() {
        return index;
    }

    public void setIndex(int newIndex) {
        index = newIndex;
    }

    public void printInfo() {
        System.out.println("Line: " + index);
        System.out.println("\tObject reference: " + super.toString());
        System.out.println("\tfrom point " + point1);
        System.out.println("\tto point " + point2);
    }
}

И мы его сериализуем, а потом восстанавливаем:
Код

public class SomeReferencesSerialization {
    public static void main(String[] args) {
        Point p1 = new Point(1.0, 1.0);
        Point p2 = new Point(2.0, 2.0);
        Point p3 = new Point(3.0, 3.0);
        Line line1 = new Line(p1, p2, 1);
        Line line2 = new Line(p2, p3, 2);
        line1.printInfo();
        line2.printInfo();
        String fileName = "file.bin";
        try {
            FileOutputStream os = new FileOutputStream(fileName);
            ObjectOutputStream oos = new ObjectOutputStream(os);

            oos.writeObject(line1);
            oos.writeObject(line2);

            line1.setIndex(11); // #1
            line2.setIndex(22); // #2

            System.out.println("After changing index");
            line1.printInfo();
            line2.printInfo();
            
            oos.writeObject(line1);
            oos.writeObject(line2);

            oos.close();
            os.close();
            System.out.println("Read objects:");
            FileInputStream is = new FileInputStream(fileName);
            ObjectInputStream ois = new ObjectInputStream(is);
            while (is.available() > 0) {
                Line line = (Line) ois.readObject();
                line.printInfo();
            }
            ois.close();
            is.close();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

В результате, несмотря на то, что после строк #1 и #2 значения полей index меняются, после десериализации они вновь равны "1" и "2" для line1 и line2 соответственно (как при изначальном создании инстансов).
Вопрос, почему это происходит?

Автор: LSD 11.12.2013, 10:21
Цитата(Artiom18 @  10.12.2013,  19:59 Найти цитируемый пост)
Вопрос, почему это происходит?

Потому что:
Цитата(Artiom18 @  10.12.2013,  19:59 Найти цитируемый пост)
И мы его сериализуем, а потом восстанавливаем:


Автор: Artiom18 11.12.2013, 10:34
LSD,  поясните, пожалуйста, тогда процесс.
Мы создаем  объект с index = 1.
Код

Line line1 = new Line(p1, p2, 1);

Первый раз мы записываем в файл его:
Код

oos.writeObject(line1);

Код

Затем:
line1.setIndex(11);

Здесь мы теоретически изменили index на 11. Это подтверждает вывод
Код

line1.printInfo();

Я не могу понять, когда мы опять получаем index = 1. При второй записи в файл или уже при десериализации? И на основании чего.
P.S. И как тогда можно сохранить измененное поле?

Автор: Artiom18 11.12.2013, 11:14
Кажется понял, в чем дело. Суть в том, что если не закрыть поток ObjectOutputStream перед вторым вызовом, то запишется не новое состояние, а объект из кэша потока.

Автор: LSD 11.12.2013, 11:15
ObjectOutputStream кеширует уже сериализованные объекты в IdentityHashMap. Это деляется для того, чтобы корректно сериализовывать деревья объектов, т.е. если есть два объекта A ссылается на B и B ссылается на A. И мы сериализуем вначале A, потом B, то при десериализации мы получим 2 объекта с взаимными ссылками, а не 4.
В твоем случае это приводит к тому, что второй раз объекты не сериализуются, а просто записывается ссылка на ранее сериализованный объект. Метод reset() решает проблему.
Код

        Line line1 = new Line(1);
        Line line2 = new Line(2);
        line1.printInfo();
        line2.printInfo();

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(line1);
        oos.writeObject(line2);
        oos.reset();
        line1.setIndex(11); // #1
        line2.setIndex(22); // #2
        System.out.println("After changing index");
        line1.printInfo();
        line2.printInfo();

        oos.writeObject(line1);
        oos.writeObject(line2);
        oos.close();


        System.out.println("Read objects:");
        ByteArrayInputStream bin = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bin);
        while (bin.available() > 0) {
            Line line = (Line) ois.readObject();
            line.printInfo();
        }
        ois.close();

Автор: Artiom18 11.12.2013, 11:26
LSD, спасибо за развернутый ответ. 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)