Есть две ключевые вещи для циклических ссылок в 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
.
Соединяя эти две вещи вместе, мы видим, что вложенный объект может получить ссылкувнешний объект.Обратите внимание, что вложенный объект видит частично сконструированный внешний объект со всеми вытекающими отсюда забавами.