Я наткнулся на кое-что, что меня заинтересовало. Судя по всему, сериализация записи с циклической ссылкой не сохраняет цикл при повторной десериализации, он становится нулевым. При выполнении того же упражнения с обычным классом цикл все еще существует.
Почему это так?
Будем признательны за любые указатели или соответствующие фрагменты из JLS.
Отказ от ответственности
Я знаю, что встроенная сериализация Java плохо и его не следует использовать, то же самое касается циклических ссылок, которые не отмечены как временные. Настройка надуманная, и я не собираюсь ее где-либо использовать. Студент столкнулся с этим, и мне любопытно, почему Java ведет себя именно так.Минимальный пример
Я свел это к настройке цикла, например:
Код: Выделить всё
normal.ref.o -> normal
record.ref().o -> record
Код: Выделить всё
import java.io.*;
import java.util.Objects;
public class Test {
static class Ref implements Serializable {
Object o;
@Override
public String toString() {
return "Ref[o=%s]".formatted(Objects.hashCode(o));
}
}
static class Normal implements Serializable {
int x;
Ref ref;
@Override
public String toString() {
return "Normal[x=%d, ref=%s]".formatted(x, ref);
}
}
record Record(int x, Ref ref) implements Serializable {}
public static void main(String[] args) throws Exception {
// Setup
Normal normalBefore = new Normal();
normalBefore.x = 5;
normalBefore.ref = new Ref();
normalBefore.ref.o = normalBefore; // cycle
// normalBefore.ref.o == normalBefore
Record recordBefore = new Record(5, new Ref());
recordBefore.ref().o = recordBefore; // cycle
// recordBefore.ref().o == recordBefore
// Test
Normal normalAfter = serializeAndDeserialize(normalBefore);
Record recordAfter = serializeAndDeserialize(recordBefore);
System.out.println("Normal before:\t" + normalBefore);
System.out.println("Normal after:\t" + normalAfter);
System.out.println("Record before:\t" + recordBefore);
System.out.println("Record after:\t" + recordAfter);
System.out.println("recordAfter.ref().o:\t" + recordAfter.ref().o);
}
static T serializeAndDeserialize(T obj) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
oos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
T result = (T) ois.readObject();
ois.close();
return result;
}
}
Код: Выделить всё
Normal before: Normal[x=5, ref=Ref[o=1732398722]]
Normal after: Normal[x=5, ref=Ref[o=1579572132]]
Record before: Record[x=5, ref=Ref[o=1323468385]]
Record after: Record[x=5, ref=Ref[o=0]]
recordAfter.ref().o: null
Однако Record удаляет циклическую обратную ссылку на себя внутри объекта-оболочки Ref, она становится нулевой.
/>Руководство по коду
Чтобы облегчить чтение кода, он состоит из оболочки цикла, ссылка:
Код: Выделить всё
static class Ref implements Serializable {
Object o;
@Override
public String toString() {
return "Ref[o=%s]".formatted(Objects.hashCode(o));
}
}
Код: Выделить всё
finalЗатем фактические классы носителей данных:
Код: Выделить всё
static class Normal implements Serializable {
int x;
Ref ref;
@Override
public String toString() {
return "Normal[x=%d, ref=%s]".formatted(x, ref);
}
}
record Record(int x, Ref ref) implements Serializable {}
Затем у нас есть метод, который просто сериализуетAndDeserialize любого заданного объекта и настройку цикла в main, а также финальный тест.
Подробнее здесь: https://stackoverflow.com/questions/797 ... ize-cycles
Мобильная версия