Сериализация и неизменяемые объекты
У меня есть класс, который предназначен для неизменного использования, поэтому я хотел бы пометить все поля final
.
Однако класс сериализуется и десериализуется для отправки по сети. Для этого требуется пустой конструктор. Это предотвращает создание конечных полей.
Я уверен, что это довольно распространенная проблема, но я не могу найти решение. Как мне продолжить?
Ответы
Ответ 1
В типичном случае сериализации не требуется, чтобы класс имел пустой конструктор или нефинализированные поля для сериализации.
Теперь, если вам нужно выполнить собственную сериализацию или вам нужно подклассифицировать класс, который не реализует Serializable, это совсем другая история.
Итак, вам нужно предоставить более подробную информацию о том, как у вас возникла проблема.
Ответ 2
Конструктор no-arg не требуется. Для самого производного класса, который не является сериализуемым, нужен конструктор no-arg, доступный для наименее всего производного сериализуемого класса.
Если вам нужно изменить поля внутри readObject
, используйте последовательный прокси через readResolve
и writeReplace
.
Ответ 3
Эта проблема является открытой ошибкой на языке Java. (Обратите внимание, что это применимо только в том случае, если вам необходимо выполнить сериализацию вручную, например, с помощью readObject)
Ответ 4
Чтобы повторить сказанное, конструкторы no-arg не являются требованием, если вы берете маршрут реализации интерфейса java.io.Serializable
. Взгляните на исходный код java.lang.Integer
, например, простой сериализуемый/неизменяемый класс, который имеет два конструктора: один, который принимает int, и тот, который принимает строку. Исходный код: http://www.docjar.com/html/api/java/lang/Integer.java.html. Javadoc: http://java.sun.com/javase/6/docs/api/java/lang/Integer.html.
Кроме того, в зависимости от сложности вашего класса и того, что вы делаете, вы можете рассмотреть возможность внедрения сериализации через интерфейс java.io.Externalizable
(хотя некоторые считают его устаревшим и для него нужен конструктор no-arg). Вот обзор SO: В чем разница между Serializable и Externalizable в Java?, а здесь официальный учебник Java: http://java.sun.com/docs/books/tutorial/javabeans/persistence/index.html.
Ответ 5
Для записи, так как у меня была аналогичная проблема:
У меня появилось сообщение "java.io.InvalidClassException: com.example.stuff.FooBar; com.example.stuff.FooBar; no valid constructor"
Я думал, что это потому, что ему не нужен конструктор по умолчанию. Но приведенные выше ответы подтверждают, что это необязательно (но наше приложение использует старый сериализатор, который действительно требует конструктора по умолчанию, поэтому может возникнуть случай).
Затем я нашел страницу с сообщением:
Если класс, предназначенный для наследования, не является сериализуемым, он может быть невозможно записать сериализуемый подкласс. В частности, это будет невозможным, если суперкласс не предоставит доступный безразмерный конструктор.
Следовательно, сообщение, которое я получил, я полагаю. Оказалось, что основная проблема была классической: я объявил класс сериализуемым, но суперкласса не было! Я переместил интерфейс Serializable в иерархию, и все было хорошо.
Но сообщение было немного ошибочным...: -)
Ответ 6
Конструктор no-arg не требуется. Пусть прочитайте исходный код:
// java.io.ObjectStreamClass
private static Constructor<?> getSerializableConstructor(Class<?> cl) {
Class<?> initCl = cl;
while (Serializable.class.isAssignableFrom(initCl)) {
if ((initCl = initCl.getSuperclass()) == null) {
return null;
}
}
...
}
Итак, на самом деле конструктор no-arg требуется в ближайшем классе Serializable
в иерархии типов.
Это означает, что следующий класс Domain
может быть сериализован.
class Domain implements Serializable {
private final int a;
public Domain(int a) {
this.a = a;
}
}
Но класс Son
не может:
class Father{
private final int a;
public Father(int a) {
this.a = a;
}
}
class Son extends Father implements Serializable {
public Son(int a) {
super(a);
}
}