Java: Когда добавлять readObjectNoData() во время сериализации?
Я читаю серию сериализации в "Эффективной Java". Я пытаюсь понять нижеследующий абзац в книге.
Если вы реализуете класс с полями экземпляров, которые сериализуются и расширяемость, есть предостережение, о котором вы должны знать. Если класс имеет инварианты, которые будут нарушены, если его поля экземпляра мы инициализируются значениями по умолчанию (ноль для интегральных типов, false для boolean и null для типов ссылок объектов), вы должен добавить этот метод readObjectNoData для класса:
// readObjectNoData for stateful extendable serializable classes
private void readObjectNoData() throws InvalidObjectException {
throw new InvalidObjectException("Stream data required");
}
Я не уверен, что означает вышеуказанное положение.
Чтобы проверить это, я создал класс Person (как сериализуемый, так и расширяемый)
class Person implements Serializable{
private String name;
private int age;
Person() {
this("default",1);
}
Person(String name, int y) {
this.name = name;
this.age = y;
}
}
и класс Employee, который расширяет его.
class Employee extends Person {
String address ;
public Employee()
{
super();
address ="default_address";
}
public Employee(String name , int age, String address)
{
super(name,age);
this.address = address;
}
}
Существуют ли какие-либо инварианты в классе Person, которые я создал? Когда они будут нарушены? Я скопировал код для метода readObjectData() в классе Employee, но он так и не был вызван. Когда будет вызван метод readObject()? Я что-то пропустил?
Ответы
Ответ 1
Раздел readObjectNoData в спецификации Java Object Serialization
кажется интересным (см. ниже).
Ваши правки на вопрос дают прекрасный пример. Если Employee
был serialized
, когда он не расширил Person
и позже deserialized
, когда он это сделал, часть Person
будет инициализирована для пустых строк и 0 age. Используя этот метод, вы можете инициализировать их "имя" и 1 соответственно.
Для сериализуемых объектов метод readObjectNoData позволяет классу для управления инициализацией собственных полей в том случае, если экземпляр подкласса десериализуется, и поток сериализации не перечислить рассматриваемый класс как суперкласс десериализованного объект. Это может происходить в случаях, когда принимающая сторона использует другая версия класса десериализованного экземпляра, чем отправляющая сторона, а версия приемника расширяет классы, которые не являются расширенный версией отправителя. Это может также произойти, если был изменен поток сериализации; следовательно, readObjectNoData - это полезно для инициализации десериализованных объектов, несмотря на "враждебный" или неполный поток источника.
private void readObjectNoData() throws ObjectStreamException;
Каждый сериализуемый класс может определять свой собственный метод readObjectNoData. Если сериализуемый класс не определяет метод readObjectNoData, тогда в перечисленных выше условиях поля класса будут инициализируются значениями по умолчанию (как указано в разделе 4.5.5 Спецификация языка JavaTM, второе издание); это поведение совместимый с ObjectInputStream до версии 1.4 JavaTM 2 SDK, стандартная версия, когда поддерживается readObjectNoData методы были введены. Если сериализуемый класс определяет readObjectNoData и вышеупомянутые условия, то readObjectNoData будет вызываться в момент десериализации когда в противном случае был бы вызван метод readObject класса рассматриваемый класс был указан потоком в качестве суперкласса экземпляр десериализован.
Ответ 2
Существуют ли какие-либо инварианты в классе Person, которые я создал? Когда они будут нарушены?
Нет явно, но представьте, что другие методы в классе предполагают, что name
никогда не null
и бросает NullPointerException
, если он когда-либо был. В этом случае ненулевое значение name
является инвариантом.
Я скопировал код для метода readObjectData()
в классе Employee
, но он никогда не вызывался. Когда вызывается метод readObject()
?
Нет метода readObjectData()
, связанного с сериализацией, это должна быть опечатка. Метод readObject()
вызывается каждый раз, когда сериализованный объект десериализуется.
Метод readObjectNoData()
попадает в какой-то неясный краевой случай при десериализации подкласса класса, который содержит этот метод.
расширенная сериализация на веб-сайте Sun Oracle посвящена цели этих вспомогательных методов сериализации. Я предлагаю вам начать работу и опубликовать любые последующие вопросы, которые могут возникнуть.
(обновление)
Если вам любопытно, метод readObjectNoData был добавлен в версию 1.4, чтобы охватить угловой случай, связанный с добавлением сериализуемого суперкласса к существующему сериализуемому классу. Подробности можно найти в спецификации сериализации Сериализация, 3.5.
Написанный текст:
Для сериализуемых объектов метод readObjectNoData позволяет классу управлять инициализацией своих собственных полей в случае десериализации экземпляра подкласса, а поток сериализации не отображает рассматриваемый класс как суперкласс объекта десериализации. Это может произойти в тех случаях, когда принимающая сторона использует другую версию класса десериализованного экземпляра, чем отправляющая сторона, а версия-получатель расширяет классы, которые не продлеваются версией отправителя. Это может также произойти, если поток сериализации был изменен; следовательно, readObjectNoData полезен для правильной инициализации десериализованных объектов, несмотря на "враждебный" или неполный поток источника.
Итак, это может произойти в двух случаях:
- JVM, который декодирует поток объектов, имеет более новую версию десериализованного подкласса (
Employee
), которая расширяет некоторый родительский класс (Person
). JVM, который изначально * en * закодировал поток объектов, имеет другую более старую версию этих классов, где Person
еще не был суперклассом Employee
.
- Кто-то намеренно испортил поток объектов, чтобы сломать вещи.
Ответ 3
"Расширяемый" означает "может иметь подкласс".
readObjectNoData используется в необычном случае, когда сериализатор (писатель) работает с версией класса без базового класса, тогда как десериализатор (считыватель) класса имеет версию класса, которая основана на подклассе, Подкласс может сказать "это нормально, если мой базовый класс не находится в сериализованных данных - просто сделайте пустой", выполнив readObjectNoData. См. Эти примечания к выпуску.
Ответ 4
Инвариант в вашем классе Person может быть примерно таким:
age must be greater than 0
Итак, когда экземпляр класса Person создается со значением по умолчанию 0 (см. здесь: http://download.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html), вы нарушите этот инвариант.
Однако, учитывая конструктор по умолчанию, вы будете в порядке:)