Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: Общие вопросы > Десериализация: зачем нужен пустой конструктор?


Автор: Dims 2.3.2009, 12:09
При десериализации объекта, система ругается InvalidClassException, no valid constructor.

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

А зачем? Получается, я должен всем классам и классам членов добавить implements Serializable и пустые конструкторы. Как-то стрёмно. Что я при этом должен обеспечить логически? Может ли быть так, что я всё это сделаю, а сериализация будет работать неправильно?

Автор: gambit 2.3.2009, 13:06
мне всегда казалось, что при вызове конструктора происходит выделение памяти необходимое объекту.

Добавлено через 1 минуту и 15 секунд
блин, я разделом ошибся.  smile 

но может и для java это справедливо?

Автор: Platon 2.3.2009, 13:26
http://ru.wikipedia.org/wiki/JavaBeans
Цитата

Класс должен иметь public конструктор без параметров. Такой конструктор позволяет инструментам создать объект без дополнительных сложностей с параметрами.

Автор: dorogoyIV 2.3.2009, 14:30
по моему компилятор должен сам создавать конструктор дефолтный - по крайней мере так написано у меня в учебнике, т.е. конструктор у класса есть всегда, даже если сам его не писал

Автор: powerOn 2.3.2009, 15:14
Цитата(dorogoyIV @  2.3.2009,  14:30 Найти цитируемый пост)
по моему компилятор должен сам создавать конструктор дефолтный 


только если нет других конструкторов. Стоит написать какой-либо с параметрами, так дефолтный не будет создан.

Автор: COVD 2.3.2009, 16:21
Цитата

А зачем?

Наверное, это позволяет восстанавливать сериализованный обьект из потока, т.е. начать не дожидаясь получения всего обьекта. Сериализованный обьект начинается с описания класса. Читается описание и создается пустой обьект. Для этого класс и должен иметь пустой конструктор. Потом идут параметры, если есть. Они последовательно читаются и вставляются в созданный обьект (через сеттеры, ведь наверное речь идет о javabean). Вторая причина - при сериализации пустые параметры ( параметр = null ) просто не пишутся в поток, т.е. минимизируется размер сериализованного обьекта.

Автор: lemonade 2.3.2009, 16:39
Dims
еще жедательно добавить serialVersionUID поле
Цитата

The serialization runtime associates with each serializable class a version number, called a serialVersionUID, which is used during deserialization to verify that the sender and receiver of a serialized object have loaded classes for that object that are compatible with respect to serialization. If the receiver has loaded a class for the object that has a different serialVersionUID than that of the corresponding sender's class, then deserialization will result in an InvalidClassException. A serializable class can declare its own serialVersionUID explicitly by declaring a field named "serialVersionUID" that must be static, final, and of type long:

 ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
 
If a serializable class does not explicitly declare a serialVersionUID, then the serialization runtime will calculate a default serialVersionUID value for that class based on various aspects of the class, as described in the Java™ Object Serialization Specification. However, it is strongly recommended that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected InvalidClassExceptions during deserialization. Therefore, to guarantee a consistent serialVersionUID value across different java compiler implementations, a serializable class must declare an explicit serialVersionUID value. It is also strongly advised that explicit serialVersionUID declarations use the private modifier where possible, since such declarations apply only to the immediately declaring class--serialVersionUID fields are not useful as inherited members.

Автор: Dims 2.3.2009, 16:46
Нет, у меня класс не следует спецификациям бинов. Получается, его нельзя сериализовать?

Мне кажется, что можно. И отсюда вопрос. Вот у меня класс с приватным параметром. Десериализатор ведь как-то умудряется его прочитать из файла и присвоить, хотя сеттера и геттера у меня к нему может и не быть. Так почему же ему нужен конструктор, чтобы создать объект?

Вопрос можно сформулировать более общо: достаточно ли удовлетворить формальным требованиям компиллятора и джавамашины к сериализуемым объектам, чтобы всё работало правильно?

Иными словами, вот у меня есть класс, который нормально, без ошибок, сериализуется и десериализуется. Могу ли я быть уверен, что всё работает правильно, или я должен ещё за чем-то проследить?

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

А в чём моя ответственность при сериализации? Какие тут могут быть логические ошибки?

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

Допустим, у меня был объект, в котором было 42 элемента. Я его сериализовал. 

Получается, при десериализации будет вызван дефолтный конструктор, то есть, создадутся 100 элементов. Затем из файла будет прочитан массив из 42 элементов.

В лучшем случае 100 элементный массив просто пойдёт в мусорку, то есть, это лишняя работа. А в худшем - возникнет какая-то ошибка.

Автор: COVD 2.3.2009, 16:54
Цитата

Иными словами, вот у меня есть класс, который нормально, без ошибок, сериализуется и десериализуется. Могу ли я быть уверен.

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

Автор: Dims 2.3.2009, 16:57
Ха, я сделал такой конструктор

Код

public Context() {
      this(1);
   }


и всё равно возникает та же ошибка!

Добавлено через 4 минуты и 22 секунды
Цитата(COVD @  2.3.2009,  16:54 Найти цитируемый пост)
наверное, основная причина в том, что неопределенные параметры обьекта не сериализуются в целях минимизации размера. 

Но это не объясняет, зачем нужен конструктор. Что мешает десериализатору просто прочитать из файла все поля и тупо присвоить их членам? А пропущенные пусть ставит в null. 

Конструктор-то как этому поможет/помешает?

Добавлено через 6 минут и 37 секунд
Блин, вот оно что: http://www.jguru.com/faq/view.jsp?EID=251942

Добавлено через 8 минут и 7 секунд
То есть, конструктор без параметров должен быть у того НАДкласса сериализуемого класса, который сам не является сериализуемым.

Автор: COVD 2.3.2009, 17:10
Цитата

Но это не объясняет, зачем нужен конструктор. Что мешает десериализатору просто прочитать из файла все поля и тупо присвоить их членам? А пропущенные пусть ставит в null. 

Вы логику разработчиков десериализатора хотите понять? Мне кажется она проста и надежна - делай раз (создай пустой обьект), делай два (если есть параметр, прочитай и засунь в обьект). Пункт два выполняется в цикле. Сериализованный обьект - это последовательность байт, которые и обрабатываются последовательно. Очевидно, именно последовательно и будет "тупо", а не "прочитать ВСЕ" и "тупо присвоить". А требование наличия пустого конструктора не выглядит обременительным.

Автор: LSD 2.3.2009, 17:26
Цитата(Dims @  2.3.2009,  16:57 Найти цитируемый пост)
Что мешает десериализатору просто прочитать из файла все поля и тупо присвоить их членам?

Необходимость того чтобы объект был корректно создан. Когда ты объявляешь объект сериализуемым ты закладываешь (по крайней мере должен) возможность того что он будет создан без вызова конструктора (тогда логика инициализации закладывается в defaultReadObject()). А вот не сериализуемый суперкласс, такого метода не имеет и потому для него вызов конструктора - обязателен.

Автор: Vitaly333 2.3.2009, 18:35

Цитата

То есть, конструктор без параметров должен быть у того НАДкласса сериализуемого класса, который сам не является сериализуемым. 


Да ,здесь всё дело именно в этом. Допустим у вас есть такой класс c несколькими конструкторами и он не реализует Serializable:

Код

Class Parent {

private int a,b;

public Parent(int a, int b){
  this.a = a;
  this.b = b;
}

public Parent(int a){
  this.a = a;
  b = a;
}


}


И есть класс его наследник:

Код


Class Chield extends Parent implements Serializable{

private c;

public Chield(int a,int b,int c){
 super(a,b);
 this.c = c;
}





Допустим вы  породили некий объект, от класса Chield и хотите его сериализовать. Так как  суперкласс не реализует Serializable, то поля a и b, унаследованные от него не будут сериализованы. Но при диссериализации их же нужно как - то инициализировать. Единственная возможность - вызвать конструктор суперкласса! Но у суперкласса определенно 2 конструктора. Возникает вопрос - а какой из них вызывать и какие значения ему передовать??? Поэтому и было принято решение , что при сериализации в суперклассе (который не реализует Serializable) обязательно должен быть определен конструктор по умолчанию(без параметров), который и осуществляет эту инициализацию.

Автор: Dims 2.3.2009, 18:46
Цитата(COVD @  2.3.2009,  17:10 Найти цитируемый пост)
Вы логику разработчиков десериализатора хотите понять? Мне кажется она проста и надежна - делай раз (создай пустой обьект)

Я нашёл ссылку. В ней написано, что это НЕ ТАК. Конструктор без параметров не нужен, что подтверждается тем, что ошибка сохранилась тогда, когда я такой конструктор создал.

Цитата(Vitaly333 @  2.3.2009,  18:35 Найти цитируемый пост)
Так как  суперкласс не реализует Serializable, то поля a и b, унаследованные от него не будут сериализованы.

Вообще-то, следования тут нет. Разработчики могли бы сделать так, чтобы это просто было запрещено. То есть, они могли потребовать, чтобы объекты Serializable обязательно имели в предках тоже Serializable, либо не имели предков вовсе (примерно так, как сделали с Cloneable). 

Но они поступили по-иному, они разрешили сериализовать объекты с несериализуемыми предками при условии, что предок имеет конструктор по умолчанию, то есть, осмысленно существование "пустого" объекта. 

Цитата(Vitaly333 @  2.3.2009,  18:35 Найти цитируемый пост)
Поэтому и было принято решение , что при сериализации в суперклассе (который не реализует Serializable) обязательно должен быть определен конструктор по умолчанию(без параметров), который и осуществляет эту инициализацию. 

Вот в таком виде это уже кажется разумным. Изначально мне это было непонятно.

Добавлено через 2 минуты и 43 секунды
Цитата(LSD @  2.3.2009,  17:26 Найти цитируемый пост)
А вот не сериализуемый суперкласс, такого метода не имеет и потому для него вызов конструктора - обязателен. 

Это уже ясно. Просто первоначально я не понял, какой именно класс должен иметь пустой конструктор. Я думал, что такой конструктор должен быть у каждого сериализуемого класса.

Мне кажется, многие из тех, кто мне отвечал, тоже так думали ;)

Автор: gosnis 2.3.2009, 19:09
Читаем javadoc интерфэйса Serializable:
Цитата

To allow subtypes of non-serializable classes to be serialized, the subtype may assume responsibility for saving and restoring the state of the supertype's public, protected, and (if accessible) package fields. The subtype may assume this responsibility only if the class it extends has an accessible no-arg constructor to initialize the class's state. It is an error to declare a class Serializable if this is not the case. The error will be detected at runtime.

То есть, у вас есть Base класс (несериализируемый) и его наследник - Derived класс. Что бы сделать Derived класс сериализируемым, в Base класс должен быть конструктор без аргументов (Base()) иначе получите ошибку во время запуска программы (runtime).

Автор: COVD 2.3.2009, 20:24
Цитата

Я нашёл ссылку. В ней написано, что это НЕ ТАК. Конструктор без параметров не нужен, что подтверждается тем, что ошибка сохранилась тогда, когда я такой конструктор создал.

Да. Я по аналогии с xml сериализацией рассуждал. Спасибо!

Автор: chand0s 4.3.2009, 11:03
Для общего развития

В Java есть 2 способа сделать объект сериализуемым: Serializable и Externalizable
1) Serializable
При сериализации: определяются поля, которые сериализуются, , значения этих полей вычитываются через reflection и записываются в выходной поток.
При десериализации: под объект выделяется место в памяти, и в эту память записываются значения из потока. Конструктор объекта не вызывается.
Какие поля сериализуются/не сериализуются: поля static и transient не сериализуются. Все остальные сериализуются. Немного отдельно стоит механизм с объявлением в классе переменной serialPersistentFields.

2) Externalizable

При сериализации: управление передается методу writeExternal сериализуемого объекта. Что писать в поток - решает сам объект.
При десериализации: создается экземпляр объекта с помощью дефолтного конструктора и вызывается метод readExternal, который вычитывает данные из потока и присваивает значения своим полям.
Какие поля можно сериализовать - а какие угодно. Вы сами это решаете в методе writeExternal.

Некоторые выводы:
1) Для реализации Serializable наличие/отсутствие каких-либо конструкторов роли не играет. Для Externalizable нужен дефолтный public-конструктор (без параметров)
2) Serializable нормально сериализует/десериализует final-поля. Externalizable этого не умеет.
3) В случае Serializable static и transient поля не сериализуются. В случае Externalizable можно записать в поток все что угодно.
4) Если класс одновременно реализует Serializable и Externalizable интерфейсы - "победит" Externalizable

Некоторые плюсы и минусы Externalizable:
Плюс: скорость сериализации/десериализации и свобода обращения с данными.
Минус: нарушение связности графа сериализуемых объектов (точнее, не нарушение, а необходимость самому за этим следить)

Есть еще ряд интересных ньюансов, но что-то пост длинный слишком получается  smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)