Сериализация Java с несериализуемыми частями - PullRequest
52 голосов
/ 18 сентября 2008

у меня есть:

class MyClass extends MyClass2 implements Serializable {
  //...
}

В MyClass2 это свойство, которое не сериализуемо. Как я могу сериализовать (и десериализовать) этот объект?

Исправление: MyClass2, конечно, не интерфейс, а класс.

Ответы [ 10 ]

49 голосов
/ 19 сентября 2008

Как кто-то еще заметил, глава 11 Effective Java Джоша Блоха является незаменимым ресурсом по сериализации Java.

Пара пунктов из этой главы, относящихся к вашему вопросу:

  • при условии, что вы хотите сериализовать состояние несериализуемого поля в MyClass2, это поле должно быть доступно для MyClass, либо напрямую, либо через методы получения и установки. MyClass должен будет реализовать пользовательскую сериализацию, предоставляя методы readObject и writeObject.
  • Класс не-сериализуемого поля должен иметь API, позволяющий получить его состояние (для записи в поток объекта), а затем создать экземпляр нового экземпляра с этим состоянием (при последующем чтении из потока объекта.)
  • в соответствии с пунктом 74 действующей Java, MyClass2 должен иметь конструктор без аргументов, доступный для MyClass, в противном случае MyClass не сможет расширить MyClass2 и реализовать Serializable.

Я написал короткий пример ниже, иллюстрирующий это.


class MyClass extends MyClass2 implements Serializable{

  public MyClass(int quantity) {
    setNonSerializableProperty(new NonSerializableClass(quantity));
  }

  private void writeObject(java.io.ObjectOutputStream out)
  throws IOException{
    // note, here we don't need out.defaultWriteObject(); because
    // MyClass has no other state to serialize
    out.writeInt(super.getNonSerializableProperty().getQuantity());
  }

  private void readObject(java.io.ObjectInputStream in)
  throws IOException {
    // note, here we don't need in.defaultReadObject();
    // because MyClass has no other state to deserialize
    super.setNonSerializableProperty(new NonSerializableClass(in.readInt()));
  }
}

/* this class must have no-arg constructor accessible to MyClass */
class MyClass2 {

  /* this property must be gettable/settable by MyClass.  It cannot be final, therefore. */
  private NonSerializableClass nonSerializableProperty;

  public void setNonSerializableProperty(NonSerializableClass nonSerializableProperty) {
    this.nonSerializableProperty = nonSerializableProperty;
  }

  public NonSerializableClass getNonSerializableProperty() {
    return nonSerializableProperty;
  }
}

class NonSerializableClass{

  private final int quantity;

  public NonSerializableClass(int quantity){
    this.quantity = quantity;
  }

  public int getQuantity() {
    return quantity;
  }
}
35 голосов
/ 18 сентября 2008

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

например:

private transient Foo foo;

Когда вы объявляете переходное поле, оно будет игнорироваться в процессе сериализации и десериализации. Помните, что при десериализации объекта с переходным полем значение этого поля всегда будет его значением по умолчанию (обычно нулевым).

Обратите внимание, что вы также можете переопределить метод readResolve () вашего класса для инициализации переходных полей на основе другого состояния системы.

15 голосов
/ 12 ноября 2012

Если возможно, несериализуемые части могут быть установлены как переходные

private transient SomeClass myClz;

В противном случае вы можете использовать Kryo . Kryo - это быстрая и эффективная инфраструктура сериализации графов объектов для Java (например, JAVA-сериализация java.awt.Color требует 170 байтов, Kryo - только 4 байта), которая может сериализовать также не сериализуемые объекты. Kryo также может выполнять автоматическое глубокое и мелкое копирование / клонирование. Это прямое копирование с объекта на объект, а не object->bytes->object.

Вот пример, как использовать kryo

Kryo kryo = new Kryo();
// #### Store to disk...
Output output = new Output(new FileOutputStream("file.bin"));
SomeClass someObject = ...
kryo.writeObject(output, someObject);
output.close();
// ### Restore from disk...
Input input = new Input(new FileInputStream("file.bin"));
SomeClass someObject = kryo.readObject(input, SomeClass.class);
input.close();

Сериализованные объекты можно также сжать, зарегистрировав точный сериализатор:

kryo.register(SomeObject.class, new DeflateCompressor(new FieldSerializer(kryo, SomeObject.class)));
11 голосов
/ 18 сентября 2008

Если вы можете изменить MyClass2, самый простой способ решить эту проблему - объявить переходное свойство.

6 голосов
/ 18 сентября 2008

Вам нужно будет реализовать writeObject() и readObject() и выполнить сериализацию / десериализацию этих полей вручную. См. Страницу javadoc для java.io.Serializable для деталей. В Effective Java Джоша Блоха также есть несколько хороших глав по реализации надежной и безопасной сериализации.

5 голосов
/ 18 сентября 2008

Зависит от того, почему этот член MyClass2 не сериализуем.

Если есть веская причина, по которой MyClass2 не может быть представлен в сериализованной форме, то велики шансы, что та же причина применима и к MyClass, поскольку это подкласс.

Может быть возможно написать настраиваемую сериализованную форму для MyClass путем реализации readObject и writeObject таким образом, что состояние данных экземпляра MyClass2 в MyClass можно соответствующим образом воссоздать из сериализованных данных. Это было бы правильным решением, если API MyClass2 исправлен и вы не можете добавить Serializable.

Но сначала вы должны выяснить, почему MyClass2 не сериализуем, и, возможно, изменить его.

4 голосов
/ 22 сентября 2008

Появилось несколько возможностей, и я возобновил их здесь:

  • Реализация writeObject () и readObject (), как sk , предложено
  • объявляет свойство временным, и оно не будет сериализовано, как указано в hank
  • использовать XStream, как указано boris-terzic
  • использовать последовательный прокси, как указано tom-hawtin-tackline
4 голосов
/ 18 сентября 2008

Вы можете начать с поиска ключевого слова transient , которое помечает поля как не являющиеся частью постоянного состояния объекта.

3 голосов
/ 18 сентября 2008

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

2 голосов
/ 19 сентября 2008

Полезный подход для сериализации экземпляров несериализуемых классов (или, по крайней мере, подклассов) известен как Serial Proxy. По сути, вы реализуете writeReplace для возврата экземпляра совершенно другого сериализуемого класса, который реализует readResolve для возврата копии исходного объекта. Я написал пример сериализации java.awt.BasicStroke на Usenet

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...