Сериализация и неизменность объектов - PullRequest
17 голосов
/ 02 ноября 2009

У меня есть класс, который предназначен для неизменного использования, поэтому я хотел бы отметить все поля final.

Однако класс сериализуется и десериализуется для отправки по сети. Для этого необходим пустой конструктор. Это мешает мне создавать последние поля.

Я уверен, что это довольно распространенная проблема, но я не могу найти решение. Как мне поступить?

Ответы [ 6 ]

9 голосов
/ 02 ноября 2009

Конструктор без аргументов не требуется. Самому производному не сериализуемому классу нужен конструктор без аргументов, доступный для наименее производного сериализуемого класса.

Если вам нужно изменить поля внутри readObject, используйте последовательный прокси через readResolve и writeReplace.

7 голосов
/ 02 ноября 2009

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

Теперь, если вам нужно выполнить свою собственную сериализацию или вам нужно создать подкласс класса, который не реализует Serializable, это другая история.

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

5 голосов
/ 02 ноября 2009

Эта проблема является открытой ошибкой на языке Java . (Обратите внимание, что это применимо, только если вам нужно выполнить сериализацию вручную, например, с readObject)

4 голосов
/ 02 ноября 2009

Чтобы повторить сказанное, конструкторы без аргументов не требуются, если вы выбираете путь реализации интерфейса java.io.Serializable. Взгляните на исходный код java.lang.Integer, например, простой сериализуемый / неизменяемый класс, который имеет два конструктора: один, который принимает int, и другой, который принимает String. Исходный код: 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 (хотя некоторые считают это устаревшим, и для него НЕ требуется конструктор без аргументов). Вот краткий обзор SO: В чем разница между Serializable и Externalizable в Java? и официальным руководством по Java: http://java.sun.com/docs/books/tutorial/javabeans/persistence/index.html.

3 голосов
/ 20 октября 2011

Для записи, так как у меня была похожая проблема:
У меня было сообщение " java.io.InvalidClassException: com.example.stuff.FooBar; com.example.stuff.FooBar; нет действительного конструктора "

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

Потом я нашел страницу, на которой было написано:

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

Отсюда и сообщение, которое я получил, наверное. Оказалось, что основная проблема была классической: я объявил класс сериализуемым, а суперкласс - нет! Я переместил интерфейс Serializable вверх в иерархии, и все было хорошо.

Но сообщение было немного вводящим в заблуждение ...: -)

2 голосов
/ 12 октября 2017

Конструктор без аргументов не требуется. Давайте прочитаем исходный код:

// java.io.ObjectStreamClass
private static Constructor<?> getSerializableConstructor(Class<?> cl) {
    Class<?> initCl = cl;
    while (Serializable.class.isAssignableFrom(initCl)) {
        if ((initCl = initCl.getSuperclass()) == null) {
            return null;
        }
    }
    ...
}

Таким образом, фактически конструктор без аргументов требуется в ближайшем не 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);
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...