当没有指定默认构造函数时,java序列化如何反序列化最终字段?

当没有指定默认构造函数时,java序列化如何反序列化最终字段?,java,serialization,final,Java,Serialization,Final,我有一个类定义了一个不可变的值类型,我现在需要序列化它。不变性来自构造函数中设置的最终字段。我试过序列化,它能工作(令人惊讶?)-但我不知道如何 下面是一个例子 public class MyValueType implements Serializable { private final int value; private transient int derivedValue; public MyValueType(int value) {

我有一个类定义了一个不可变的值类型,我现在需要序列化它。不变性来自构造函数中设置的最终字段。我试过序列化,它能工作(令人惊讶?)-但我不知道如何

下面是一个例子

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”检查警告,但是成功地为我刚刚使其可序列化的其他类生成了警告。)

反序列化是由JVM在低于基本语言构造的级别上实现的。具体来说,它不调用任何构造函数

假定类没有无参数构造函数,如何实例化它并设置最终字段

一些令人讨厌的黑魔法发生了。JVM中有一个后门,允许在不调用任何构造函数的情况下创建对象。新对象的字段首先初始化为其默认值(false、0、null等),然后对象反序列化代码使用对象流中的值填充字段


(既然Java是开源的,你就可以阅读实现这一点的代码了……然后哭泣!)

Michael和Stephen都给了你一个很好的答案,我只想提醒你关于
瞬态
字段

如果反序列化后默认值(
null
表示引用,0表示原语)对它们不可接受,则您必须提供您的
readObject
版本并在那里进行初始化

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

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

实际上,它确实调用了无参数构造函数。只有具有此类构造函数的类才可序列化。不是真的。您可以在没有默认构造函数的情况下序列化类。@Alexander:我的错。我把它与可序列化类型的不可序列化超类必须有这样一个构造函数的事实混淆了。@Oak:对,所以Michael的回答实际上有点不准确(这与这个问题无关)。反序列化确实调用层次结构中最后一个不可序列化类的构造函数。事实上,您甚至不需要讨厌的黑魔法,反射(也称为讨厌的灰色魔法;)可以用来设置最终字段。只要在第一次读取字段之前设置了值,它就可以工作。@gustafc-但是在不执行任何构造函数的情况下创建对象需要黑魔法。。。。这一点很重要,因为任何构造函数的执行都可能产生不必要的副作用。不管怎样,如果足够的话,您也可以在字段声明中指定一个值。例如,随机生成的uuid