Tuesday, 3 July 2012

Serializing an object which has a non-serializable parent class

An object is serializable (itself implements the Serializable interface) even if its superclass is not.When you serialize an object, the serialization mechanism works by chaining up the inheritence hierarchy, saving the sate of each Serializable superclass in turn. When serialization reaches the first non-serializable superclass, the serialization stops.Only the fields of Serializable objects are written out to the stream.

When deserializing, the state of this first non-serializable superclass is restored not from the stream, but by invoking that class' no-argument constructor.The no-arg contructor of every non-serializable superclass will run when an object is deserialized and that will initialize the fields of non-serializable supertypes.The first non-serializable superclass must have a no-arg constructor and that must be accessible to the subclass that is serializable. If this is violated, readObject() will produce a java.io.InvalidClassException at run time.

Let us see one example,

The serializable Employee class inheriting a non-serializable class Person.


package serialize;

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 NonSerializeParentExample {

 /**
  * @param args
  * @throws ClassNotFoundException
  **/
 public static void main(String[] args) throws ClassNotFoundException {

  ObjectOutputStream out = null;
  ObjectInputStream in = null;
  try {
   out = new ObjectOutputStream(new FileOutputStream("employee.ser"));

   /** Create one Employee object to serialise **/

   Employee emp = new Employee("John", 12345, "Banking");

   /** Serialize the object emp and write to file employee.ser **/

   out.writeObject(emp);

   in = new ObjectInputStream(new FileInputStream("employee.ser"));

   /** Deserialize the object stored in the file employee.ver **/

   Employee emp_copy = (Employee) in.readObject();

   System.out.println(emp_copy);

  } catch (IOException e) {
   e.printStackTrace();
  } finally {
   try {
    out.close();
    in.close();
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
 }

}

/** Non-serializable parent class **/

class Person {

 String empName;

 /**
  * mandatory public non-arg constructor. It will be invoked during
  * deserialization process.
  **/
 public Person() {

  System.out
    .println("Deserialization: Invoking non-arg Person() constructor.");
  System.out
    .println("Initializing non-serializable base class field 'empName'");
  empName = "NotAvailable";
 }

 public Person(String empName) {
  this.empName = empName;
 }

 /** Override the toString method to print object fields **/

 public String toString() {
  return ("Employee Name =" + empName);
 }
}

/** Serializable sub class **/

class Employee extends Person implements Serializable {

 /**
  * Generated serialVersionUID for versioning of serialised objects.
  * 
  **/
 private static final long serialVersionUID = -417056492237332874L;

 int empno;
 String dept;

 public Employee(String empName, int empno, String dept) {
  super(empName);
  this.empno = empno;
  this.dept = dept;
 }

 public Employee() {

 }

 /** Override the toString method to print object fields **/

 public String toString() {
  return ("Employee Name =" + empName + " Employee No =" + empno
    + " & Department =" + dept);
 }

}

The output will be

Deserialization: Invoking non-arg Person() constructor.
Initializing non-serializable base class field 'empName'
Employee Name =NotAvailable Employee No =12345 & Department =Banking

As we discussed above the non-serializable parent class field, 'empName' does't save during serialization,only the fields of the serializable sub class will be stored.The non-arg constructor of the non-serializable super-class will run when an object is deserialized and that will initialize the value of super class field 'empName' with value 'NotAvailable'.

If the no-argument constructor is not adequate for your purposes, you must customize the serialization of your subclass with writeObject(ObjectOutputStream) and readObject(ObjectInputStream) in order to write out and restore any information from the non-serializable superclass that you find necessary.See the example given below.

Example: Customize the Default Protocol


In this example we are declaring writeObject and readObject methods inside the serializable sub class Employee for explicitly saving and restoring the non-serializable parent class field 'empName' .

package serialize;

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 CustomSerialization {

 /**
  * @param args
  * @throws ClassNotFoundException
  **/
 public static void main(String[] args) throws ClassNotFoundException {

  ObjectOutputStream out = null;
  ObjectInputStream in = null;
  try {
   out = new ObjectOutputStream(new FileOutputStream("employee.ser"));

   /** Create one Employee object to serialise **/

   Employee emp = new Employee("John", 12345, "Banking");

   /** Serialise the object emp and write to file employee.ser **/

   out.writeObject(emp);

   in = new ObjectInputStream(new FileInputStream("employee.ser"));

   /** Deserialise the object stored in the file employee.ver **/

   Employee emp_copy = (Employee) in.readObject();

   System.out.println(emp_copy);

  } catch (IOException e) {
   e.printStackTrace();
  } finally {
   try {
    out.close();
    in.close();
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
 }

}

/** Non-serializable parent class **/

class Person {

 String empName;

 /**
  * mandatory public non-arg constructor. It will be invoked during
  * deserialization process.
  **/
 public Person() {
  System.out.println("Deserialization:Invoking non-arg Person() constructor");
 }

 public Person(String empName) {
  this.empName = empName;
 }

 /** Override the toString method to print object fields **/

 public String toString() {
  return ("Employee Name =" + empName);
 }
}

/** Serializable sub class **/

class Employee extends Person implements Serializable {

 /**
  * Generated serialVersionUID for versioning of serialised objects.
  * 
  **/
 private static final long serialVersionUID = -417056492237332874L;

 int empno;
 String dept;

 public Employee(String empName, int empno, String dept) {
  super(empName);
  this.empno = empno;
  this.dept = dept;
 }

 public Employee() {

 }

 /** Override the toString method to print object fields **/

 public String toString() {
  return ("Employee Name =" + empName + "Employee No =" + empno
    + " & Department =" + dept);
 }

 /**
  * Declared to customize the deserialization process. The nonserialized
  * super class field 'empName' will be restored explicitly.
  **/

 private void readObject(ObjectInputStream in) throws IOException,
   ClassNotFoundException {

  // Restore the sub class fields.
  in.defaultReadObject();

  System.out
    .println("Deserialization: Restoring Non-Serializable parent class field explicitly.");
  
  // explicitly do the restoring for super class field.
  empName = in.readUTF();

 }

 /**
  * Declared to customize the serialization process. The nonserialized super
  * class field 'empName' will be saved explicitly.
  **/

 private void writeObject(ObjectOutputStream out) throws IOException {

  // Save the sub class fields.
  out.defaultWriteObject();

  System.out
    .println("Serialization: Saving Non-Serializable parent class field explicitly.");

  // explicitly do the saving for super class field.
  out.writeUTF(empName);
 }

}
The output will be

Serialization: Saving Non-Serializable parent class field explicitly.
Deserialization: Invoking non-arg Person() constructor 
Deserialization: Restoring Non-Serializable parent class field explicitly.
Employee Name =John Employee No =12345 & Department =Banking

You can see that value of the non-serializable parent class field 'empName' saved and restored successfully.

No comments:

Post a Comment