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

mdm*_*dma 49 java serialization final

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

这是该课程的一个例子

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...
}
Run Code Online (Sandbox Code Playgroud)

鉴于该类没有没有arg构造函数,它如何实例化并设置最终字段?

(旁边 - 我注意到这个类特别是因为IDEA没有为这个类生成"no serialVersionUID"检查警告,但是成功地为我刚刚可序列化的其他类生成了警告.)

Mic*_*rdt 42

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

  • @橡木.不对.您可以在没有默认构造函数的情况下序列化类. (19认同)
  • @Oak:对,所以迈克尔的答案实际上有点不准确(并不是因为这个问题的目的而重要).反序列化确实调用层次结构中最后一个非可序列化类的构造函数. (14认同)
  • @Alexander:我的坏.我把它与可序列化类型的非可序列化超类必须具有这样的构造函数这一事实混淆了. (4认同)
  • 实际上,它确实调用无参数构造函数.只有具有这种构造函数的类才是可序列化的. (2认同)

Ste*_*n C 22

鉴于该类没有没有arg构造函数,它如何实例化并设置最终字段?

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

(现在Java是开源的,你可以阅读执行此操作的代码......然后哭!)

  • @gustafc - 但是在不执行任何构造函数的情况下创建对象需要黑魔法. (7认同)
  • 事实上,你甚至不需要令人讨厌的黑魔法,反射(又名讨厌的灰色魔法;)可以用来设置最终的领域.只要在第一次读取字段之前设置了值,它就会起作用. (6认同)

Ale*_*yak 12

迈克尔和斯蒂芬都给了你一个很好的答案,我只想提醒你关于transient领域.

如果null在反序列化后默认值(对于引用,0表示基元)对于它们是不可接受的,那么您必须提供您的版本readObject并在那里初始化它.

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

        // derivedValue is still 0
        this.derivedValue = derivedValue( value );
    }
Run Code Online (Sandbox Code Playgroud)