Подтвердить что ты не робот

Как сериализация java десериализует конечные поля, если не задан конструктор по умолчанию?

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

Здесь пример класса

public class MyValueType implements Serializable
{
    private final int value;

    private transient int derivedValue;

    public MyValueType(int value)
    {
        this.value = value;
        this.derivedValue = derivedValue(value);
    }

    // getters etc...
}

Учитывая, что класс не имеет конструктора arg, как он может быть создан и задано конечное поле?

(В стороне - я заметил этот класс, особенно потому, что IDEA не генерировал предупреждение об отсутствии "serialVersionUID" для этого класса, но успешно создало предупреждения для других классов, которые я только что сделал сериализуемыми.)

4b9b3361

Ответ 1

Deserialization реализуется JVM на уровне ниже базовых языковых конструкций. В частности, он не вызывает никакого конструктора.

Ответ 2

Учитывая, что класс не имеет конструктора arg, как он может быть создан и задано конечное поле?

Происходит какая-то неприятная черная магия. В JVM есть бэкдор, который позволяет создавать объект без вызова какого-либо конструктора. Поля нового объекта сначала инициализируются значениями по умолчанию (false, 0, null и т.д.), А затем код десериализации объекта заполняет поля значениями из потока объектов.

(Теперь, когда Java открыта, вы можете прочитать код, который делает это... и плакать!)

Ответ 3

И Майкл, и Стивен дали отличный ответ, я просто хочу предупредить вас о transient.

Если значение по умолчанию (null для ссылок, 0 для примитивов) неприемлемо для них после десериализации, вы должны предоставить свою версию readObject и инициализировать его там.

    private void readObject (
            final ObjectInputStream s
        ) throws
            ClassNotFoundException,
            IOException
    {
        s.defaultReadObject( );

        // derivedValue is still 0
        this.derivedValue = derivedValue( value );
    }