Сохраняет ли сериализация идентичность объекта? - PullRequest
22 голосов
/ 29 августа 2009

Я использую интерфейс Java Serializable и ObjectOutputStream для сериализации объектов (до сих пор этого метода было достаточно для моих целей).

Мой API полагается на идентификацию объекта для некоторых операций, и мне интересно, будет ли он сохранен при сериализации. То есть: , если для двух произвольных объектов a и b он содержит a == b до сериализации, он все еще сохраняется после десериализации?

Я нашел несколько текстов, которые утверждают обратное - но они либо написали о более старой версии JRE (меня интересует только 1.6 и, возможно, 1.5), либо были связаны с RMI ( что для меня не актуально).

Документация не очень готова к идентификации объекта. В технической статье на sun.com упоминается, что ObjectOutputStream использует кэширование объектов, что для меня имеет смысл только в том случае, если идентичность объекта действительно сохраняется, но я не достаточно уверен, чтобы полагаться на это неубедительное доказательство.

Я опробовал его (Java 1.6, OS X) и обнаружил, что yes , идентичность объектов остается неизменной при сериализации . Но могу ли я экстраполировать эти результаты или они ненадежны?

Для моего теста я сериализовал следующий граф объектов:

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);
    }
}

Ответы [ 2 ]

18 голосов
/ 29 августа 2009

Для двух произвольных объектов a и b, если он содержит a == b перед сериализацией, он останется верным после десериализации IF:

  1. И a, и b записываются как и впоследствии считываются как части одного и того же потока. Вот цитата из ObjectInputStream документации: «Графики объектов восстанавливаются правильно с использованием механизма совместного использования ссылок ."
  2. Класс a и b не переопределяет readResolve(), который может изменить способ восстановления ссылок; а также классы, содержащие a и b.

Во всех остальных случаях идентичность объекта НЕ будет сохранена.

10 голосов
/ 29 августа 2009

Ответ: нет , по умолчанию идентификация объекта не сохраняется при сериализации, если вы рассматриваете две отдельные сериализации данного объекта / графика. Например, если я сериализую объект по сети (возможно, я отправляю его от клиента на сервер через RMI), а затем делаю это снова (в отдельных вызовах RMI), тогда 2 десериализованных объекта на сервере будут не быть ==.

Однако, в «одиночной сериализации», например одиночное сообщение клиент-сервер, представляющее собой граф, содержащий несколько раз один и тот же объект после десериализации, идентичность сохраняется .

Для первого случая, однако, вы можете предоставить реализацию метода readResolve, чтобы обеспечить возвращение правильного экземпляра (например, в typesafe enum шаблон). 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;
    }
}

.

...