为啥基类(未实现 Serializable)如果其子类实现 Serializable,则不应有参数构造函数?

Posted

技术标签:

【中文标题】为啥基类(未实现 Serializable)如果其子类实现 Serializable,则不应有参数构造函数?【英文标题】:Why base class(not implementing Serializable) should have no argument constructor if its subclass implements Serializable?为什么基类(未实现 Serializable)如果其子类实现 Serializable,则不应有参数构造函数? 【发布时间】:2021-06-19 18:24:57 【问题描述】:

我正在阅读接口Serializable 的文档,我在其中找到以下几行:

为了允许对不可序列化类的子类型进行序列化,子类型可能会负责保存和恢复超类型的公共、受保护和(如果可访问)包字段的状态。仅当它扩展的类具有可访问的无参数构造函数来初始化类的状态时,子类型才可以承担此责任。如果不是这种情况,则声明类 Serializable 是错误的。将在运行时检测到错误。

但是基类的无参数构造函数在恢复对象状态中的作用是什么?

【问题讨论】:

见jguru.com/faq/view.jsp?EID=251942 【参考方案1】:

当您尝试反序列化可序列化对象时,该机制必须创建该对象的一个​​空实例并填写成员,以将该对象恢复到序列化时的状态。可序列化对象的构造函数在第一次构造对象时会被调用,但在反序列化过程中不会调用构造函数,因为从技术上讲,您不是在构造对象,而是将其重构到以前的状态。构造和子序列操作的任何影响都应该已经被合并到对象状态中。

每当你构造任何类的对象时,Java 都必须调用超类的构造函数,以及超超类等。你可以使用super(...) 为超类指定特定的构造函数,或者如果你不指定超级构造函数,将使用默认构造函数。一种或另一种方式,所有到根的类都被构造出来。

可序列化对象的反序列化不会导致构造函数调用,但是当存在不可序列化的超类时(即您扩展具有可序列化类的不可序列化类)则该类不期望被反序列化,并且它没有存储/恢复其成员的机制。如果父类不可序列化,则反序列化机制需要调用零参数构造函数来确保重构的对象实例正确初始化。

如果您未能指定零参数构造函数,反序列化代码将不会警告您此问题,直到您第一次尝试反序列化该类的对象。编译时没有警告。

此外,您的可序列化子类必须负责存储/恢复不可序列化超类中的任何成员值。

【讨论】:

基类的构造函数总是序列化运行吗? 如果超类是可序列化的,那么不需要 arg 构造函数对吗?因为派生类对象会自动保存基类部分,因为该部分是可序列化的。不是吗。] 如果超类是可序列化的,那么该机制将首先反序列化超类,然后反序列化被引用类的附加细节。由于超类被标记为可序列化,编译器已确保它具有执行此操作的所有正确部分。 通常每个类只调用一个构造函数。因此,如果您有多个构造函数,则反序列化将仅使用零参数构造函数。一个构造函数使用this() 语句显式调用另一个构造函数时例外。 这个答案具有误导性。反序列化 Serializable 类不会调用该类定义的任何构造函数。我没有投反对票,因为您的 cmets 似乎表明您理解这一点,并且仅对不可序列化的类调用构造函数。如果这不是您的意思,请仔细阅读serialization spec 的第 11.a 节。【参考方案2】:

如果超类不是可序列化的,而不是序列化子类的对象,我们必须在子类中显式地实现可序列化接口。在这种情况下,超类中必须有一个无参数的构造函数。

如果超类不是Serializable,那么从超类继承的所有实例变量的值都将在反序列化过程中通过调用Non-Serializable超类的构造函数进行初始化。

【讨论】:

以上是关于为啥基类(未实现 Serializable)如果其子类实现 Serializable,则不应有参数构造函数?的主要内容,如果未能解决你的问题,请参考以下文章

为啥要实现Serializable

为啥要实现Serializable

序列化继承:如果基类没有标记[Serializable]会抛出异常吗?

为啥 Comparator 应该实现 Serializable?

HttpServlet为啥要实现Serializable?

Java 之 序列化接口