序列化和不可变对象

时间:2009-11-02 19:19:53

标签: java serialization final

我有一个用于不可变用途的类,因此我想标记所有字段final

但是,类被序列化并反序列化以通过网络发送。为此,需要一个空构造函数。这可以防止我创建最终字段。

我确信这是一个相当普遍的问题,但我找不到解决方案。我该怎么办?

6 个答案:

答案 0 :(得分:9)

不需要无参数构造函数。派生程度最高的非可序列化类确实需要一个no-arg构造函数可用于最少派生的可序列化类。

如果您需要改变readObject中的字段,请通过readResolvewriteReplace使用序列代理。

答案 1 :(得分:7)

在典型的序列化情况下,不需要该类具有可构造的空构造函数或非最终字段。

现在,如果你必须自己进行序列化,或者你需要子类化一个没有实现Serializable的类,那就是另一个故事了。

因此,您需要提供有关如何解决问题的更多详细信息。

答案 2 :(得分:5)

此问题是open bug on the Java language。 (请注意,这仅适用于必须手动执行序列化的操作,例如使用readObject)

答案 3 :(得分:4)

为了回应已经说过的内容,如果您采用实现java.io.Serializable接口的路线,则不需要使用非arg构造函数。看一下java.lang.Integer源代码,例如,一个简单的可序列化/不可变类,它有两个构造函数:一个接受一个int,另一个接受一个String。源代码:http://www.docjar.com/html/api/java/lang/Integer.java.html。 Javadoc:http://java.sun.com/javase/6/docs/api/java/lang/Integer.html

此外,根据您的类的复杂性和您正在做的事情,您可以考虑通过java.io.Externalizable接口实现序列化(尽管有些人认为它过时了,并且它需要一个无参数构造函数)。以下是SO的概述:What is the difference between Serializable and Externalizable in Java?,这是官方Java教程:http://java.sun.com/docs/books/tutorial/javabeans/persistence/index.html

答案 4 :(得分:3)

为了记录,我有一个类似的问题:
我有一条消息“ java.io.InvalidClassException:com.example.stuff.FooBar; com.example.stuff.FooBar; no valid constructor

我认为这是因为它缺少默认构造函数。但上面的答案证实它不是强制性的(但我​​们的应用程序使用了一个确实需要默认构造函数的旧序列化程序,因此可能出现这种情况)。

然后我找到一个页面说明:

  

如果为继承而设计的类不可序列化,那么   可能无法编写可序列化的子类。具体来说,它   如果超类不提供可访问的将是不可能的   无参数构造函数。

因此我猜到了这个消息。似乎核心问题是经典的:我声明一个类是可序列化的,但是超类不是!我在层次结构中移动了Serializable接口,一切都很顺利。

但是这条消息有点误导......: - )

答案 5 :(得分:2)

不需要无参数构造函数。让我们阅读源代码:

// java.io.ObjectStreamClass
private static Constructor<?> getSerializableConstructor(Class<?> cl) {
    Class<?> initCl = cl;
    while (Serializable.class.isAssignableFrom(initCl)) {
        if ((initCl = initCl.getSuperclass()) == null) {
            return null;
        }
    }
    ...
}

因此,实际上在类型层次结构中最近的非Serializable类中需要no-arg构造函数。

这意味着可以序列化以下类Domain

class Domain implements Serializable {
    private final int a;

    public Domain(int a) {
      this.a = a;
    }
}

但是班级Son可以&#t; t:

class Father{
  private final int a;

  public Father(int a) {
    this.a = a;
  }
}

class Son extends Father implements Serializable {
  public Son(int a) {
    super(a);
  }
}