简体   繁体   English

为什么在反序列化过程中不调用默认构造函数?

[英]Why default constructor is not called while deserialization process?

ObjectInputStream is = new ObjectInputStream(new FileInputStream("test.ser"));
TestClass tc = (TestClass)is.readObject();

I get the object of TestClass after deserialization but TestClass default constructor is not called. 我在反序列化后得到了TestClass的对象,但是没有调用TestClass的默认构造函数。 As per my understanding there are two ways to create object ie either with new operator or TestClass.class.newInstance(). 根据我的理解,有两种方法可以创建对象,即使用new运算符或TestClass.class.newInstance()。 Both calls the default constructor. 两者都调用默认构造函数。

Looks like deserialization process creates the object not with about two methods thats why default constructor is not called. 看起来像反序列化过程创建对象不是用两个方法,这就是为什么不调用默认构造函数。 Question is how deserialization creates the object ? 问题是反序列化如何创建对象?

Another point is if TestClass extends BaseTestClass and BaseTestClass does not implements serialization , constructor of BaseTestClass is called but not TestClass. 另一点是,如果TestClass扩展BaseTestClass并且BaseTestClass没有实现序列化,则调用BaseTestClass的构造函数但不调用TestClass。 Why so ? 为什么这样 ? I am sure there will be some logical reason behind it. 我相信它背后会有一些合理的理由。 But i am not getting it? 但我没有得到它?

It's worth reading Java Object Serialization Specification: 3 - Object Input Classes where readObject method is described in details along with step by step explanation. 值得阅读Java对象序列化规范:3 - 对象输入类 ,其中详细描述了readObject方法以及逐步说明。

How it works? 这个怎么运作?

An instance of the class is allocated. 分配了一个类的实例。 The instance and its handle are added to the set of known objects. 实例及其句柄将添加到已知对象集中。

The contents restored appropriately: 内容适当恢复:

  1. For serializable objects, the no-arg constructor for the first non-serializable supertype is run . 对于可序列化对象, 运行第一个非可序列化超类型的no-arg构造函数

    • For serializable classes, the fields are initialized to the default value appropriate for its type. 对于可序列化的类,字段将初始化为适合其类型的默认值。

    • Then the fields of each class are restored by calling class-specific readObject methods, or if these are not defined, by calling the defaultReadObject method. 然后通过调用类特定的readObject方法来恢复每个类的字段,或者如果没有定义它们,则通过调用defaultReadObject方法来恢复。

    • Note that field initializers and constructors are not executed for serializable classes during deserialization . 请注意, 在反序列化期间,不会对可序列化类执行字段初始值设定项和构造函数

    • In the normal case, the version of the class that wrote the stream will be the same as the class reading the stream. 在正常情况下,编写流的类的版本将与读取流的类相同。 In this case, all of the supertypes of the object in the stream will match the supertypes in the currently-loaded class. 在这种情况下,流中对象的所有超类型都将匹配当前加载的类中的超类型。

    • If the version of the class that wrote the stream had different supertypes than the loaded class, the ObjectInputStream must be more careful about restoring or initializing the state of the differing classes. 如果编写流的类的版本具有与加载的类不同的超类型,则ObjectInputStream必须更加小心地恢复或初始化不同类的状态。

    • It must step through the classes, matching the available data in the stream with the classes of the object being restored. 它必须逐步执行这些类,将流中的可用数据与要还原的对象的类进行匹配。 Data for classes that occur in the stream, but do not occur in the object, is discarded. 流中出现但未在对象中出现的类的数据将被丢弃。

    • For classes that occur in the object, but not in the stream, the class fields are set to default values by default serialization. 对于在对象中但不在流中出现的类,默认序列化将类字段设置为默认值。

  2. For externalizable objects, the no-arg constructor for the class is run and then the readExternal method is called to restore the contents of the object. 对于可外部化的对象,运行该类的no-arg构造函数,然后调用readExternal方法以恢复该对象的内容。


Sample code to understand fist point For serializable objects, the no-arg constructor for the first non-serializable supertype is run. 用于理解第一点的示例代码对于可序列化对象,运行第一个非可序列化超类型的无参数构造函数。

Sample code; 示例代码;

class TestClass1 {
    public TestClass1() {
        System.out.println("TestClass1");
    }
}

class TestClass2 extends TestClass1 implements Serializable {
    public TestClass2() {
        System.out.println("TestClass2");
    }
}

public static void main(String[] args) throws Exception {
    System.out.println("Object construction via calling new keyword");
    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("resources/dummy.dat"));
    out.writeObject(new TestClass2());

    System.out.println("Object construction via readObject method");
    ObjectInputStream is = new ObjectInputStream(new FileInputStream("resources/dummy.dat"));
    TestClass2 tc = (TestClass2) is.readObject();
}

output: 输出:

Object construction via calling new keyword
TestClass1
TestClass2

Object construction via readObject method
TestClass1

Note: this relates to Externalizable classes, not Serializable , as correctly indicated by Pshemo in the comments below. 注意:这与Externalizable类有关,而不是Serializable ,正如Pshemo在下面的评论中正确指出的那样。 Answer posted by Braj shows a code sample for Serializable . Braj发布的答案显示了Serializable的代码示例。


First of all, note the difference between a default constructor and a no-arg constructor. 首先,请注意默认构造函数和无参数构造函数之间的区别。 Default constructor is a no-arg constructor generated if you don't provide any other constructor. 如果不提供任何其他构造函数,则默认构造函数是生成的无参数构造函数。

ObjectInputStream requires that a class has a no-arg constructor, here is a code sample that demonstrates it: ObjectInputStream要求类具有无参数构造函数,这是一个演示它的代码示例:

import java.util.*;
import java.lang.*;
import java.io.*;

class Ideone
{
  static class Test implements Externalizable
  {
    //public Test() {}

    public Test(int x)
    {
    }

    public void writeExternal(ObjectOutput out)
        throws IOException
    {
    }

    public void readExternal(ObjectInput in)
        throws IOException, ClassNotFoundException
    {
    }
}

public static void main(String[] args)
    throws java.lang.Exception
  {
    Test t = new Test(0);
    ByteArrayOutputStream os = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(os);
    oos.writeObject(t);
    oos.close();

    ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
    ObjectInputStream ois = new ObjectInputStream(is);
    t = (Test)ois.readObject();
    ois.close();
  }
}

Produces: 生产:

Exception in thread "main" java.io.InvalidClassException: Ideone$Test; 线程“main”中的异常java.io.InvalidClassException:Ideone $ Test; no valid constructor at java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(ObjectStreamClass.java:147) at java.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:755) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1751) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1347) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:369) at Ideone.main(Main.java:36) 在java.io.Object.StartEream上没有有效的构造函数$ ExceptionInfo.newInvalidClassException(ObjectStreamClass.java:147),java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1751)中的java.io.Object.StreamClass.checkDeserialize(ObjectStreamClass.java:755)在java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1347)处于Ideone.main的java.io.ObjectInputStream.readObject(ObjectInputStream.java:369)(Main.java:36)

Demo on Ideone: http://ideone.com/yPpJrb Ideone上的演示: http ://ideone.com/yPpJrb

When you uncomment the no-arg constructor, it works fine. 取消注释no-arg构造函数时,它可以正常工作。 And it will also work fine when you'll remove the provided one-argument constructor - because then, the default constructor will be generated. 当你删除提供的单参数构造函数时,它也会正常工作 - 因为那时,将生成默认构造函数。

from the oracle document 来自oracle文档

Reading an object from the ObjectInputStream is analogous to creating a new object. 从ObjectInputStream读取对象类似于创建新对象。 Just as a new object's constructors are invoked in the order from the superclass to the subclass, an object being read from a stream is deserialized from superclass to subclass. 正如从超类到子类的顺序调用新对象的构造函数一样,从流中读取的对象从超类反序列化为子类。 The readObject or readObjectNoData method is called instead of the constructor for each Serializable subclass during deserialization. 在反序列化期间,将调用readObject或readObjectNoData方法而不是每个Serializable子类的构造函数。

so in nutshell it should call readObject() method in the hierarchy from super class to subclass. 所以简而言之,它应该在从超类到子类的层次结构中调用readObject()方法。 which will only present if all super class implements serializable interface, otherwise superclasse's default constructor is invoked. 只有当所有超类都实现了可序列化接口时才会出现,否则会调用superclasse的默认构造函数。 so serializable 如此序列化

Each subclass of a serializable object may define its own readObject method. 可序列化对象的每个子类可以定义自己的readObject方法。 If a class does not implement the method, the default serialization provided by defaultReadObject will be used. 如果类未实现该方法,则将使用defaultReadObject提供的默认序列化。 When implemented, the class is only responsible for restoring its own fields, not those of its supertypes or subtypes. 实现时,该类仅负责恢复其自己的字段,而不是其超类型或子类型的字段。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM