Записи не десериализуют циклыJAVA

Программисты JAVA общаются здесь
Ответить
Anonymous
 Записи не десериализуют циклы

Сообщение Anonymous »

Вопрос
Я наткнулся на кое-что, что меня заинтересовало. Судя по всему, сериализация записи с циклической ссылкой не сохраняет цикл при повторной десериализации, он становится нулевым. При выполнении того же упражнения с обычным классом цикл все еще существует.
Почему это так?
Будем признательны за любые указатели или соответствующие фрагменты из 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
Как мы видим, Normal полностью сериализует и десериализует (полная печать toString() приведет к StackOverflow из-за цикла).
Однако Record удаляет циклическую обратную ссылку на себя внутри объекта-оболочки Ref, она становится нулевой.
/>Руководство по коду
Чтобы облегчить чтение кода, он состоит из оболочки цикла, ссылка:

Код: Выделить всё

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 {}
Int x технически устарел, но я добавил его, чтобы показать, что нециклы по-прежнему полностью десериализуются.
Затем у нас есть метод, который просто сериализуетAndDeserialize любого заданного объекта и настройку цикла в main, а также финальный тест.

Подробнее здесь: https://stackoverflow.com/questions/797 ... ize-cycles
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «JAVA»