Сохраняет ли сериализация идентификатор объекта?
Я использую интерфейс Java Serializable
и ObjectOutputStream
для сериализации объектов (до сих пор этого метода было достаточно для моих целей).
Мой API полагается на идентификатор объекта для некоторых операций, и мне интересно, сохранится ли он путем сериализации. То есть: , если для двух произвольных объектов a
и b
он сохраняет a == b
перед сериализацией, сохраняется ли она после десериализации?
Ive нашел несколько текстов, которые утверждают об обратном - но они либо писали о более старой версии JRE (Im интересовался только 1.6 и, возможно, 1.5), или были связаны с RMI (что не имеет для меня значения).
Документация не очень подходит для идентификации объекта. Техническая статья на sun.com упоминает, что ObjectOutputStream
использует кеширование объектов, что для меня имеет смысл только в том случае, если идентичность объекта действительно сохранена, но Я не достаточно уверен, чтобы полагаться на это необоснованное доказательство.
Ive попробовал это (Java 1.6, OS X) и обнаружил, что да, идентификация объектов остается неизменной путем сериализации. Но могу ли я экстраполировать эти результаты или они ненадежны?
Для моего теста Ive сериализовал следующий граф объектов:
C----------+
| b1 b2 |
+----------+
| |
v v
B---+ B---+
| a | | a |
+---+ +---+
\ /
\ /
\/
A----+
| |
+----+
Минимальный код воспроизведения:
import java.io.*;
public class SerializeTest {
static class A implements Serializable {}
static class B implements Serializable {
final A a;
public B(A a) {
this.a = a;
}
}
static class C implements Serializable {
final B b1, b2;
public C() {
A object = new A();
b1 = b2 = new B(object);
}
}
public static void main(String[] args) throws IOException,
ClassNotFoundException {
C before = new C();
System.out.print("Before: ");
System.out.println(before.b1.a == before.b2.a);
// Serialization.
ByteArrayOutputStream data = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(data);
out.writeObject(before);
out.close();
// Deserialization.
ObjectInputStream in =
new ObjectInputStream(new ByteArrayInputStream(data.toByteArray()));
C after = (C) in.readObject();
System.out.print("After: ");
System.out.println(after.b1.a == after.b2.a);
}
}
Ответы
Ответ 1
Для двух произвольных объектов a и b, если он содержит a == b перед сериализацией, он будет сохраняться и после десериализации IF:
- Оба a и b записываются как и впоследствии считываются из частей одного и того же потока. Здесь приведена цитата из ObjectInputStream:" Графы объектов восстанавливаются корректно с помощью механизма обмена ссылками.
- Класс a и b не переопределяет
readResolve()
, который имеет потенциал изменения способа восстановления ссылок; ни классы, которые содержат a и b.
Для всех остальных случаев идентификация объекта НЕ будет сохранена.
Ответ 2
Ответ нет, по умолчанию идентификация объекта не сохраняется через сериализацию, если вы рассматриваете две отдельные сериализации данного объекта/графика. Например, если я сериализую объект по проводке (возможно, я отправлю его от клиента на сервер через RMI), а затем повторю его (в отдельных вызовах RMI), то 2 десериализованных объекта на сервере не будут ==.
Однако, в случае "одиночной сериализации", например, одно сообщение клиент-сервер, которое представляет собой график, содержащий один и тот же объект несколько раз, а затем при десериализации, сохраняется идентичность.
В первом случае вы можете, однако, обеспечить реализацию метода readResolve
, чтобы убедиться, что правильный экземпляр (например, в шаблоне перечисления типов). readResolve
- это частный метод, который будет вызываться JVM на де-сериализованном Java-объекте, предоставляя объекту возможность вернуть другой экземпляр. Например, таким образом, как TimeUnit
enum
может быть реализовано до того, как enum
были добавлены в язык:
public class TimeUnit extends Serializable {
private int id;
public TimeUnit(int i) { id = i; }
public static TimeUnit SECONDS = new TimeUnit(0);
//Implement method and return the relevant static Instance
private Object readResolve() throws ObjectStreamException {
if (id == 0) return SECONDS;
else return this;
}
}
.