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

StackOverflow https://stackoverflow.com/questions/95181

  •  01-07-2019
  •  | 
  •  

Вопрос

У меня есть:

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

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

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

Это было полезно?

Решение

Как заметил кто-то еще, глава 11 книги Джоша Блоха Эффективная 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;
  }
}

Другие советы

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

бывший:

private transient Foo foo;

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

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

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

private transient SomeClass myClz;

В противном случае вы можете использовать Крио.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)));

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

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

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

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

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

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

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

Появилось несколько вариантов, и я возвращаюсь к ним здесь:

  • Реализуйте writeObject() и readObject() как SK предложенный
  • объявите свойство временным, и оно не будет сериализовано, как сначала указано хэнк
  • используйте XStream, как указано в борис-терзич
  • используйте последовательный прокси-сервер, как указано в том-хотин-линия захвата

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top