Добавление конструктора без параметров в несериализуемый, расширяемый класс

#java #serialization

#java #сериализация

Вопрос:

Что особенного в добавлении конструктора без параметров к несериализуемому, расширяемому классу.

В Effective java автор рассказывает об этой теме.

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

Следующий код скопирован из действующей версии Java 2nd[Страница 292-293]

  public class AbstractFoo {
private int x, y; // Our state

// This enum and field are used to track initialization
private enum State {
    NEW, INITIALIZING, INITIALIZED
};

private final AtomicReference<State> init = new AtomicReference<State>(
        State.NEW);

public AbstractFoo(int x, int y) {
    initialize(x, y);
}

// This constructor and the following method allow
// subclass's readObject method to initialize our state.
protected AbstractFoo() {
}

protected final void initialize(int x, int y) {
    if (!init.compareAndSet(State.NEW, State.INITIALIZING))
        throw new IllegalStateException("Already initialized");
    this.x = x;
    this.y = y;
    // ... // Do anything else the original constructor did
    init.set(State.INITIALIZED);
}

// These methods provide access to internal state so it can
// be manually serialized by subclass's writeObject method.
protected final int getX() {
    checkInit();
    return x;
}

protected final int getY() {
    checkInit();
    return y;
}

// Must call from all public and protected instance methods
private void checkInit() {
    if (init.get() != State.INITIALIZED)
        throw new IllegalStateException("Uninitialized");
}
// ... // Remainder omitted

    }
  

Все общедоступные и защищенные методы экземпляра в AbstractFoo должны вызывать
Проверьте его, прежде чем делать что-либо еще. Это гарантирует, что вызовы методов завершатся неудачей
быстро и чисто, если плохо написанному подклассу не удается инициализировать экземпляр. Примечание
что инициализированное поле является атомарной ссылкой (java.util.concurrent.
атомарный.Атомарная ссылка). Это необходимо для обеспечения целостности объекта в
лицо решительного противника. В отсутствие этой меры предосторожности, если один поток
должны были вызвать initialize в экземпляре, в то время как второй поток пытался использовать
при этом второй поток может увидеть экземпляр в несогласованном состоянии.

Зачем мы это делаем? Я не до конца понял это. Кто-нибудь может объяснить?

Ответ №1:

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

http://docs.oracle.com/javase/7/docs/api/java/io/Serializable.html

Читать это. Согласно этому «Во время десериализации поля несериализуемых классов будут инициализированы с использованием общедоступного или защищенного конструктора класса без аргументов. Конструктор без аргументов должен быть доступен для сериализуемого подкласса. Поля сериализуемых подклассов будут восстановлены из потока»

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

Ответ №2:

Отдельный метод инициализации очень полезен, когда у вас проблема с многопоточностью. Вы можете посмотреть хорошую статью : http://www.ibm.com/developerworks/java/library/j-jtp0618/index.html