Подтвердить что ты не робот

Сериализация Java с несериализуемыми частями

У меня есть:

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

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

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

4b9b3361

Ответ 1

Как заметил еще один, глава 11 Джоша Блоха Эффективная Java является незаменимым ресурсом для Java Serialization.

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

  • Предполагая, что вы хотите сериализовать состояние несериализуемого поля в MyClass2, это поле должно быть доступно для MyClass, либо напрямую, либо через геттеры и сеттеры. MyClass должен будет реализовать пользовательскую сериализацию, предоставляя методы readObject и writeObject.
  • несериализуемое поле Класс должен иметь API, позволяющий получать его состояние (для записи в поток объектов), а затем создавать экземпляр нового экземпляра с этим состоянием (при последующем чтении из потока объектов).
  • за элемент 74 эффективного Java, MyClass2 должен иметь конструктор no-arg, доступный для 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;
  }
}

Ответ 2

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

Пример:

private transient Foo foo;

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

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

Ответ 3

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

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

Ответ 4

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

Ответ 5

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

Ответ 6

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

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

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

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

Ответ 7

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

Ответ 8

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

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

Ответ 9

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

Ответ 10

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