Как сериализация Java десериализует конечные поля, если не указан конструктор по умолчанию? - PullRequest
47 голосов
/ 25 мая 2010

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

Вот пример класса

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...
}

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

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

Ответы [ 3 ]

39 голосов
/ 25 мая 2010

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

19 голосов
/ 25 мая 2010

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

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

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

12 голосов
/ 25 мая 2010

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

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

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

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