Reputation: 34424
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. As per my understanding there are two ways to create object i.e either with new operator or 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. Why so ? I am sure there will be some logical reason behind it. But i am not getting it?
Upvotes: 4
Views: 4148
Reputation:
from the oracle document
Reading an object from the ObjectInputStream is analogous to creating a new object. 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.
so in nutshell it should call readObject() method in the hierarchy from super class to subclass. which will only present if all super class implements serializable interface, otherwise superclasse's default constructor is invoked. so serializable
Each subclass of a serializable object may define its own readObject method. If a class does not implement the method, the default serialization provided by defaultReadObject will be used. When implemented, the class is only responsible for restoring its own fields, not those of its supertypes or subtypes.
Upvotes: 0
Reputation: 46861
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.
An instance of the class is allocated. The instance and its handle are added to the set of known objects.
The contents restored appropriately:
For serializable objects, the no-arg constructor for the first non-serializable supertype is run.
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.
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.
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.
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.
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
Upvotes: 9
Reputation: 35901
Note: this relates to Externalizable
classes, not Serializable
, as correctly indicated by Pshemo in the comments below. Answer posted by Braj shows a code sample for 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:
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; 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)
Demo on Ideone: http://ideone.com/yPpJrb
When you uncomment the no-arg constructor, it works fine. And it will also work fine when you'll remove the provided one-argument constructor - because then, the default constructor will be generated.
Upvotes: 0