我有一个用于不可变用途的类,因此我想标记所有字段final
。
但是,类被序列化并反序列化以通过网络发送。为此,需要一个空构造函数。这可以防止我创建最终字段。
我确信这是一个相当普遍的问题,但我找不到解决方案。我该怎么办?
答案 0 :(得分:9)
不需要无参数构造函数。派生程度最高的非可序列化类确实需要一个no-arg构造函数可用于最少派生的可序列化类。
如果您需要改变readObject
中的字段,请通过readResolve
和writeReplace
使用序列代理。
答案 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);
}
}