Как я могу связаться, два Java сериализировали объекты назад вместе?

Иногда (довольно много, на самом деле) мы получаем ситуацию в Java, где два объекта указывают на то же самое. Теперь, если мы сериализируем их отдельно, довольно уместно, чтобы сериализированные формы имели отдельные копии объекта, поскольку должно быть возможно открыться один без другого. Однако, если мы теперь десериализовываем их обоих, мы находим, что они все еще разделяются. Там какой-либо путь состоит в том, чтобы связать их назад вместе?

Пример следует.

public class Example {

 private static class ContainerClass implements java.io.Serializable {
  private ReferencedClass obj;
  public ReferencedClass get() {
   return obj;
  }
  public void set(ReferencedClass obj) {
   this.obj = obj;
  }
 }

 private static class ReferencedClass implements java.io.Serializable {
  private int i = 0;
  public int get() {
   return i;
  }
  public void set(int i) {
   this.i = i;
  }
 }

 public static void main(String[] args) throws Exception {
  //Initialise the classes
  ContainerClass test1 = new ContainerClass();
  ContainerClass test2 = new ContainerClass();
  ReferencedClass ref = new ReferencedClass();

  //Make both container class point to the same reference
  test1.set(ref);
  test2.set(ref);

  //This does what we expect: setting the integer in one (way of accessing the) referenced class sets it in the other one
  test1.get().set(1234);
  System.out.println(Integer.toString(test2.get().get()));

  //Now serialise the container classes
  java.io.ObjectOutputStream os = new java.io.ObjectOutputStream(new java.io.FileOutputStream("C:\\Users\\Public\\test1.ser"));
  os.writeObject(test1);
  os.close();
  os = new java.io.ObjectOutputStream(new java.io.FileOutputStream("C:\\Users\\Public\\test2.ser"));
  os.writeObject(test2);
  os.close();

  //And deserialise them
  java.io.ObjectInputStream is = new java.io.ObjectInputStream(new java.io.FileInputStream("C:\\Users\\Public\\test1.ser"));
  ContainerClass test3 = (ContainerClass)is.readObject();
  is.close();
  is = new java.io.ObjectInputStream(new java.io.FileInputStream("C:\\Users\\Public\\test2.ser"));
  ContainerClass test4 = (ContainerClass)is.readObject();
  is.close();

  //We expect the same thing as before, and would expect a result of 4321, but this doesn't happen as the referenced objects are now separate instances
  test3.get().set(4321);
  System.out.println(Integer.toString(test4.get().get()));
 }

}
10
задан Kidburla 26 April 2010 в 21:50
поделиться

3 ответа

Метод readResolve () позволяет это (из Конечно, сначала вы должны определить, как вы собираетесь решить, какие объекты должны быть «одинаковыми»). Но гораздо проще было бы сериализовать оба объекта в один и тот же файл - ObjectOut / InputStream хранит запись всех объектов, которые он сериализовал / десериализовал, и будет хранить и возвращать ссылки только на те объекты, которые он уже видел.

3
ответ дан 4 December 2019 в 03:38
поделиться

Как и в приведенных выше ответах, readResolve является ключом, поскольку он позволяет вам заменить «повторяющийся» объект на тот, который вам нужен.

Предполагая, что ваш класс реализует hashCode () и equals (), вы реализуете дедупликацию, создав статическую WeakHashMap, которая содержит все ссылки на созданные объекты, все еще находящиеся в памяти. например

class ReferencedClass implements Serializable
{
   static private Map<ReferencedClass, Reference<ReferencedClass>> map = new WeakHashMap<ReferencedClass, Reference<ReferencedClass>>;

   static public ReferencedClass findOriginal(ReferencedClass obj)
   {
      WeakReference<ReferencedClass> originalRef = map.get(obj);
      ReferencedClass original = originalRef==null ? null : originalRef.get();
      if (original==null)
      {
          original = obj;
          map.put(original, new WeakReference<ReferencedClass>(original));
      }
      return original;
   }

   static public ReferencedClass()
   {
        findOriginal(this);
   }

   private Object readResolve()
   {
       return findOriginal(this);
   }
}

При десериализации readResolve () вызывает RerencedClass.findOriginal (this) для получения текущего исходного экземпляра. Если экземпляры этого класса создаются только путем десериализации, то это будет работать как есть. Если вы также создаете объекты (используя оператор new), ваши конструкторы также должны вызывать findOriginal, передавая это, чтобы эти объекты также добавлялись в пул.

После внесения этих изменений оба экземпляра ContainerClass будут указывать на один и тот же экземпляр ReferenceClass , даже если они были десериализованы независимо.

1
ответ дан 4 December 2019 в 03:38
поделиться

Я сделал что-то подобное для создаваемой мной базы данных сервера приложений / объектов. Каковы ваши требования - зачем вам это нужно? Если ваши требования - это нечто меньшее, чем сервер приложений, то, возможно, какой-то другой дизайн решит это проще.


Если вы все же хотите продолжить, вот как это сделать:

Сначала вам нужно подключиться к процессу сериализации, переопределив ObjectOutputStream.replaceObject () и ObjectInputStream.resolveObject () методы. См. Мой пример ObjectSerializer .

Когда вы сериализуете объекты, вы должны назначить уникальный идентификатор для каждого экземпляра объекта, которому вы хотите присвоить уникальную идентичность - такие объекты обычно называются сущностями. Когда объект ссылается на другие сущности, вы должны заменить эти другие сущности объектом-заполнителем, который содержит идентификатор упомянутой сущности.

Затем, когда объекты десериализованы, вы должны заменить каждый из этих объектов-заполнителей на реальный объект-объект, имеющий этот идентификатор. Вам необходимо отслеживать экземпляры объекта сущности, которые были загружены в память, и их идентификаторы, чтобы для каждого идентификатора создавался только один экземпляр. Если объект еще не загружен в память, вы должны загрузить его из того места, где он был сохранен. См. Мой пример EntityManager .

Если вы хотите выполнить отложенную загрузку, чтобы избежать загрузки всего графа объекта в память, когда он не нужен, вы должны сделать что-то подобное прозрачным ссылкам . См. Их реализацию здесь . Если вы зашли так далеко, вы также можете скопировать эти части (пакеты entity , entity.tref , serial и, возможно, также context ) из моего проекта - он имеет разрешающую лицензию - и измените их в соответствии со своими потребностями (т. Е. Удалите ненужные вам вещи).

1
ответ дан 4 December 2019 в 03:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: