Как сериализация Java решает проблемы циклических ссылок? - PullRequest
0 голосов
/ 13 октября 2018

Я сделал сериализационный тест с Java.Я обнаружил, что сериализация Java может правильно обрабатывать циклические ссылки.Но как сериализация Java решает проблемы циклических ссылок?

Следующий код работает правильно:

public class SerializableTest {

    static class Employee implements Serializable{

        private static final long serialVersionUID = 1L;

        String name;

        int age;

        Employee leader;

        public void say(){
            System.out.println("my name is " + name + ". and I'm " + age + " years old.");
        }

    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {

        ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream(new File("tempPath")));

        Employee employee = new Employee();
        employee.name = "Tom";
        employee.age = 41;
        employee.leader = employee;
        employee.say();

        objectOutput.writeObject(employee);

        ObjectInput objectInput = new ObjectInputStream(new FileInputStream(new File("tempPath")));

        Employee readEmployee = (Employee) objectInput.readObject();

        readEmployee.say();
        readEmployee.leader.say();
    }
}

Ответы [ 3 ]

0 голосов
/ 13 октября 2018

Есть две ключевые вещи для циклических ссылок в Java-сериализации: обратные ссылки и вложение конструкций.

Java-сериализация выполняет первичный обход глубины.Рассмотрим этот пример.

class Outer implements java.io.Serializable {
    Inner inner;
}

Один объект содержит другой (при условии, что inner не равен нулю).Внешний объект начинает записываться в поток, в середине которого записывается внутренний объект, за которым следует остальная часть внешнего объекта.Аналогично для чтения.Сериализация Java не задерживает запись внешнего объекта до тех пор, пока внутренние объекты не будут аккуратно построены.

По аналогии с обычным кодом Java можно создавать вложенные объекты в конструкторе внешних объектов.

    // Like Java Serialization
    Outer() {
        this.inner = new Inner();
    }

Вместо создания вложенных объектов и передачи ссылок на конструктор внешнего объекта.

    // Not like Java Serialization
    Outer(Inner inner) {
        this.inner = inner;
    }
}

    ... new Outer(new Inner()) ...

Обратные ссылки необходимы, даже если вы просто хотели получить ациклический ориентированный граф объектов.Рассмотрим простой пример.

class Foo implements java.io.Serializable {
    Object a = new SerialBar();
    Object b = a;
}

Мы должны найти для любого десериализованного экземпляра foo, foo.a == foo.b.Чтобы добиться этой сериализации, проверяется, запускал ли поток сериализованную ссылку ранее, и, если это так, вставляет обратную ссылку, а не повторную сериализацию объекта.Объект запоминается, как только он создается, но до начала сериализации поля по умолчанию или readObject / readExternal.

Соединяя эти две вещи вместе, мы видим, что вложенный объект может получить ссылкувнешний объект.Обратите внимание, что вложенный объект видит частично сконструированный внешний объект со всеми вытекающими отсюда забавами.

0 голосов
/ 17 октября 2018

Я нашел ответ в книге «Core Java® Volume II - Расширенные функции, десятое издание» . В разделе 2.4 «Потоки ввода / вывода объектов и сериализация» этогокнига, описание:

За кулисами ObjectOutputStream просматривает все поля объектов и сохраняет их содержимое.Например, при записи объекта Employee поля имени, даты и зарплаты записываются в выходной поток.

Однако есть одна важная ситуация, которую мы должны рассмотреть: что происходит, когда один объект является общимнесколькими объектами как частью своего состояния?

Сохранение такой сети объектов является сложной задачей.Конечно, мы не можем сохранять и восстанавливать адреса памяти для объектов секретаря.Когда объект перезагружается, он, вероятно, будет занимать совершенно другой адрес памяти, чем изначально.

Вместо этого каждый объект сохраняется с серийным номером, отсюда и сериализация имени объекта для этого механизма.Вот алгоритм:

  1. Свяжите серийный номер с каждой ссылкой на объект, с которой вы столкнулись (как показано на рисунке 2.6).

  2. При первом обращении к ссылке на объект сохраните данные объекта в выходной поток.

  3. Если он был сохранен ранее, просто напишите «как у ранее сохраненного объекта с серийным номером x».

При чтении обратнообъекты, процедура обратная.

  1. Когда объект указывается во входном потоке объекта в первый раз, создайте его, инициализируйте его данными потока и запомните связь междусерийный номер и ссылка на объект.

  2. Когда встречается тег «такой же, как ранее сохраненный объект с серийным номером x», получить ссылку на объект для порядкового номера.

Таким образом, это сериализация и десериализация графа объектов таким способом с JAVA.

0 голосов
/ 13 октября 2018

Сериализация Java использует IdentityHashMap для сопоставления каждой ссылки, которую она пытается сериализовать, с идентификатором.При первом сериализации объекта он записывает его содержимое и идентификатор.После этого он записывает только идентификатор, позволяющий циклические ссылки и одну копию объекта, независимо от того, сколько раз на него ссылаются.

Недостатком является то, что если вы сохраняете поток объекта и не вызываете reset()он сохранит каждый отправленный вами объект, что приведет к увеличению использования памяти.Кроме того, если вы измените объект и отправите его снова, изменения не будут очевидны, поскольку он только снова отправит ссылку на объект.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...