【问题标题】:Serialising and immutable objects序列化和不可变对象
【发布时间】:2009-11-02 19:19:53
【问题描述】:

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

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

我确定这是一个相当普遍的问题,但我找不到解决方案。我应该如何进行?

【问题讨论】:

    标签: java serialization final


    【解决方案1】:

    不需要无参数构造函数。派生最多的不可序列化类确实需要一个无参数构造函数,该构造函数可用于派生最少的可序列化类。

    如果您需要更改 readObject 中的字段,请通过 readResolvewriteReplace 使用串行代理。

    【讨论】:

      【解决方案2】:

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

      现在,如果您必须自己进行序列化,或者您需要子类化一个不实现 Serializable 的类,那就另当别论了。

      因此,您需要提供更多关于您遇到问题的详细信息。

      【讨论】:

      • 谢谢,我使用的是典型的序列化方法,但总是提供一个空的构造函数,因为我认为它是这样工作的。
      【解决方案3】:

      这个问题是open bug on the Java language。 (请注意,这只适用于必须手动进行序列化的情况,例如使用 readObject)

      【讨论】:

      • 来自评估:“问题适用于类的可序列化字段以外的最终实例字段”,因此在标准情况下它可以正常工作。尼克似乎在做一些不同的事情。
      • 啊,是的,我应该添加一个免责声明,这仅适用于您必须挂钩 readObject 或类似的东西。
      【解决方案4】:

      为了回应已经说过的内容,如果您采用实现java.io.Serializable 接口的路线,则不需要无参数构造函数。例如,看一下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

      【讨论】:

        【解决方案5】:

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

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

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

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

        因此我得到了消息,我想。看来核心问题是经典的:我声明一个类是可序列化的,但超类不是!我将 Serializable 接口在层次结构中向上移动,一切都很好。

        但是这个信息有点误导...... :-)

        【讨论】:

          【解决方案6】:

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

          // 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 类中需要无参数构造函数。

          表示下面的类Domain可以序列化。

          class Domain implements Serializable {
              private final int a;
          
              public Domain(int a) {
                this.a = a;
              }
          }
          

          但是Son的班级不能:

          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);
            }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-04-08
            • 1970-01-01
            • 2014-02-10
            • 1970-01-01
            相关资源
            最近更新 更多