2

Why default constructor is not invoked/called during deserialization mechanism?

Followed the discussion and comments but didn't find satisfactory answer.

I understand during the deserialization process

It creates the instance of Object class by invoking its default constructor and with the help of created instance, it creates instance of Parent and Child class using newConstructorForSerialization() method of ReflectionFactory class which internally create instance without invoking constructor of class.

What is the reason of implementing in this way?

 public class EmployeeAddress/* implements Serializable */{

/**
 * 
 */
/*private static final long serialVersionUID = 4455988544392627515L;*/
protected String address;

public EmployeeAddress() {
    System.out.println(this.address);
    System.out.println("EmployeeAddress constructor has invoked with no-args");
}

public EmployeeAddress(String address) {
    this.address = address;
    System.out.println(this.address);
    System.out.println("EmployeeAddress constructor has invoked with args");
}

// setter / getter
}

public class Employee extends EmployeeAddress implements Serializable{
/**
 * 
 */
/*public static final long serialVersionUID = 151736201136818635L;*/
private static final long serialVersionUID = 2L;

private String name;
private int age;
private String designation;
private double salary;
private PermanentEmployee pEmpoyee;

private transient long empId;

/*public Employee(String address) {super(address);}*/

public Employee(String name, int age, PermanentEmployee pEmpoyee, String designation, String address, long empId) {
    super(address);
    this.name = name;
    this.age = age;
    this.pEmpoyee = pEmpoyee;
    this.designation = designation;
    this.salary = 2000.00 + pEmpoyee.getBonus();
    this.empId = empId;
}

// setter / getter
}


public class PermanentEmployee implements Serializable{
/**
 * 
 */
private static final long serialVersionUID = -6153718980149785271L;

private double bonus;

public PermanentEmployee() {}

public PermanentEmployee(double bonus) {
    this.bonus = bonus;
}

// setter / getter
}


public class SerializationDemo {
public static void main(String[] args) {

    PermanentEmployee pEmpoyee = new PermanentEmployee(1000.00);

    com.serialization.first.Employee emp = new com.serialization.first.Employee("XYY", 30, pEmpoyee, "Leader", "Nagpur", 2001);
    WriterAndReader.write(emp, "emp.ser");

    System.out.println("Serailization processed..for com.serialization.first.Employee ");


    com.serialization.first.Employee emp1  = (com.serialization.first.Employee) WriterAndReader.read("emp.ser");

    System.out.println("Name: "+ emp1.getName() 
    + ", Age: "+ emp1.getAge() 
    + ", Designation: "+ emp1.getDesignation()
    + ", Salary: " + emp1.getSalary()
    + ", Address: " + emp1.getAddress()
    + ", EmpId: " + emp1.getEmpId());

    System.out.println("Deserailization processed.. for com.serialization.first.Employee ");
}
}

Output:

null
EmployeeAddress constructor has invoked with no-args
Name: XYY, Age: 30, Designation: Leader, Salary: 3000.0, Address: null, EmpId: 0
Deserailization processed.. for com.serialization.first.Employee
Indra G
  • 21
  • 5
  • 1
    Because that's the way they designed it. Unclear what sort of an answer would satisfy you, or why you're asking it here, unless you think you'll get a Java author answering. It isn't likely. – user207421 Apr 18 '19 at 07:45

2 Answers2

2

Because it is the way serialisation works. It acts as an hidden constructor, as explained by Joshua Bloch in "Effective Java": https://qtips.github.io/2017/08/effective-java-chapter-11

There is a mechanism available as a "replacement" to the constructor call, with readObject and readResolve methods. There, you can handle stream corruption/manipulation, protect your class invariants and restore the state of non serializable fields: Java serialization: readObject() vs. readResolve()

For the reason behind, one I can think of quickly is that not all classes declare a no-arg constructor. Thus you can not rely on it to create objects of any type.

spi
  • 1,673
  • 13
  • 19
  • You don't need to write these methods, and 'handle stream corruption' isn't the same thing as 'protect your class invariants', and I don't see anything in your second link that says otherwise. It would be perfectly possible for `Serializable` to *require* a no-args constructor, as `Externalizable` does. – user207421 Apr 18 '19 at 07:45
  • @user207421 better now? Ps: even if it could be possible for Serializable to require no-arg constructor, that is not the case actually. – spi Apr 18 '19 at 07:46
2

Why default constructor is not invoked/called during deserialization mechanism?

A serialized object is the just the bytecode of what the object was in the memory before it was serialized. So when the object is deserialized, it's exactly the same object. It appears to be already created, and memory is allocated for the same. It's not the actual creation of the object. So the constructor will not be called.

Consider the output of this class :

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class Serialization {
    public static void main(String args[]) throws IOException, ClassNotFoundException {
        SomeClass sc = new  SomeClass(2,"sdkjvb"); 
        FileOutputStream fos = new FileOutputStream("serialization.ser");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(sc);
        oos.close();


        FileInputStream fis = new FileInputStream("serialization.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        SomeClass sc1 = (SomeClass) ois.readObject();
        sc1.setS("xjdhj");
        ois.close();
        System.out.println(sc);
        System.out.println(sc1);
    }
}

class SomeClass implements Serializable{

    private static final long serialVersionUID = 1L;
    private int x;
    private String s;
    public SomeClass(int x, String s) {
        super();
        this.x = x;
        this.s = s;
    }
    public void setS(String s) {
        this.s = s;
    }
    @Override
    public String toString() {
        return s+"--->"+x;
    }
}

This is the content of .ser file :

aced 0005 7372 0019 4f62 6a65 6374 4f75 7453 7472 6561 6d2e 536f 6d65 436c 6173 7300 0000 0000 0000 0102 0002 4900 0178 4c00 0173 7400 124c 6a61 7661 2f6c 616e 672f 5374 7269 6e67 3b78 7000 0000 0274 0006 7364 6b6a 7662

and this is the output:

sdkjvb--->2
xjdhj--->2

Only one object was created by calling the Constructor. The other was just replicated using the bytecode of the previous one.

raviiii1
  • 936
  • 8
  • 24