Java – Serialization with Inheritance

In this article, we will discuss Serialization with Inheritance i.e.; IS-A relationship with inheriting class in detail

It’s an easy choice, when both super-class & sub-class are Serializable, because

  • When superclass is serializable, properties of super-class will be serialized
  • When subclass is serializable, properties of sub-class as well as inherited properties of super-class will also be serialized

But we need to understand 2 scenarios with respect to IS-A relationship, while serializing & de-serializing sub-class, when

  1. Super-class implements java.io.Serializable but sub-class doesn’t implement java.io.Serializable
  2. Sub-class implements java.io.Serializable but super-class doesn’t implement java.io.Serializable

Let’s us discuss serialization with inheritance with 2 demo program

1. Serialization with Inheritance

Case 1: Super-class implements java.io.Serializable but sub-class doesn’t implement java.io.Serializable

  • When super-class is serializable, then any class that is extending super-class will also be serializable by default through inheritance principle
  • So, here sub-class doesn’t required to implement java.io.Serializable explicitly
  • When sub-class is serialized, then sub-class properties as well as inherited super-class properties will also be serialized during serialization process
  • Note: To prevent sub class from serializing by default, then we need to override writeObject() and readObject() methods

Step 1.1: Create super-class Customer implementing java.io.Serializable interface

  • For any class said to be serializable, then it must implement java.io.Serializable interface
  • Otherwise, NotSerializableException will be thrown at run time, although program compiles successfully
  • Overrides toString() method to print values in desired format

Customer.java

package in.bench.resources.serialization.inheritance;

import java.io.Serializable;

class Customer implements Serializable {

	// instance variables
	int customerId;
	String customerName;

	// overriding toString() method
	@Override
	public String toString() {
		return "Customer ["
				+ "customerId=" + customerId
				+ ", customerName=" + customerName
				+ "]";
	}
}

Step 1.2: Create sub-class PrivilegedCustomer extending super-class Customer

  • For any class said to be serializable, then it must implement java.io.Serializable interface
  • But here, sub-class PrivilegedCustomer is also serializable by default, although sub-class doesn’t implement java.io.Serializable interface explicitly
  • Because super-class implements serializable interface through inheritance principle
  • If any class doesn’t implements serializable interface, then NotSerializableException will be thrown at run time, although program compiles successfully
  • Overrides toString() method to print values in desired format

PrivilegedCustomer.java

package in.bench.resources.serialization.inheritance;

class PrivilegedCustomer extends Customer {

	// instance variables
	float discountRate;
	int bonusPoints;

	@Override
	public String toString() {
		return "PrivilegedCustomer ["
				+ "customerId=" + customerId
				+ ", customerName=" + customerName
				+ ", discountRate=" + discountRate
				+ ", bonusPoints=" + bonusPoints
				+ "]";
	}
}

As we are ready with POJOs implementing java.io.Serializable, we will begin with our serialization and de-serialization process from main class

Step 1.3: Serialization and De-Serialization (with Inheritance)

  • For any class said to be serializable, then it must implement java.io.Serializable interface directly/indirectly though inheritance
  • Otherwise, NotSerializableException will be thrown at run time, although program compiles successfully
  • To Serialize any Object, we can use ObjectOutputStream & FileOutputStream to write/save to file in binary format
  • To De-Serialize any Object, we can use ObjectInputStream & FileInputStream to read/restore from file (which is in binary format) into Java heap memory
  • Serializing & De-Serializing sub-class PrivilegedCustomer

SerializationWithInheritance.java

package in.bench.resources.serialization.inheritance;

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

public class SerializationWithInheritance {

	public static void main(String[] args) {

		// creating Privileged Customer object
		PrivilegedCustomer serializePrivilegedCustomer =
				new PrivilegedCustomer();

		// initialize values for privileged customer object
		serializePrivilegedCustomer.customerId = 101;
		serializePrivilegedCustomer.customerName = "SJ";
		serializePrivilegedCustomer.discountRate = 12.5f;
		serializePrivilegedCustomer.bonusPoints = 1000;

		// time to play with Serialization and De-Serialization process

		// creating output stream variables
		FileOutputStream fos = null;
		ObjectOutputStream oos = null;

		// creating input stream variables
		FileInputStream fis = null;
		ObjectInputStream ois = null;

		// creating customer object reference
		// to hold values after de-serialization
		Customer deSerializePrivilegedCustomer = null;

		try {
			// for writing or saving binary data
			fos = new FileOutputStream("CustomerInheritance.ser");

			// converting java-object to binary-format
			oos = new ObjectOutputStream(fos);

			// writing or saving customer object's value to stream
			oos.writeObject(serializePrivilegedCustomer);
			oos.flush();
			oos.close();

			System.out.println("Serialization: Privileged Customer "
					+ "object saved to CustomerInheritance.ser file\n");

			// reading binary data
			fis = new FileInputStream("CustomerInheritance.ser");

			// converting binary-data to java-object
			ois = new ObjectInputStream(fis);

			// reading object's value and casting to Customer class
			deSerializePrivilegedCustomer =
					(PrivilegedCustomer) ois.readObject();
			ois.close();

			System.out.println("De-Serialization: "
					+ "Privileged Customer object de-serialized "
					+ "from CustomerInheritance.ser file\n");
		}
		catch (FileNotFoundException fnfex) {
			fnfex.printStackTrace();
		}
		catch (IOException ioex) {
			ioex.printStackTrace();
		}
		catch (ClassNotFoundException ccex) {
			ccex.printStackTrace();
		}

		// printing customer object to console using toString() method
		System.out.println("Printing privilege customer values "
				+ "from de-serialized object... \n"
				+ deSerializePrivilegedCustomer);
	}
}

Output:

Serialization: Privileged Customer object saved to
CustomerInheritance.ser file

De-Serialization: Privileged Customer object de-serialized
from CustomerInheritance.ser file

Printing privilege customer values from de-serialized object...
PrivilegedCustomer [customerId=101, customerName=SJ,
discountRate=12.5, bonusPoints=1000]

Case 2: Sub-class implements java.io.Serializable but super-class doesn’t implement java.io.Serializable

  • Before moving ahead, we should understand whether is it possible to serializable sub-class, if it’s super-class isn’t serializable ?
  • The answer is yes, because if the condition to serialize any class on the basis of its super-class implementing java.io.Serializable interface, then no class in Java can be serialized
  • Reason: java.lang.Object is the base class for any class defined in Java, and it doesn’t implements java.io.Serializable interface
  • In that way, it is very well possible to serialize a sub-class even if its super-class doesn’t implement java.io.Serializable interface

Step 2.1: Create super-class Customer which doesn’t implement java.io.Serializable interafce

  • For any class said to be serializable, it must implement java.io.Serializable interface
  • Otherwise, NotSerializableException will be thrown at run time, although program compiles successfully
  • Overrides toString() method to print values in desired format

Customer.java

package in.bench.resources.serialization.inheritance;

class Customer {

	// instance variables
	int customerId;
	String customerName;

	// overriding toString() method
	@Override
	public String toString() {
		return "Customer [customerId=" + customerId
				+ ", customerName=" + customerName + "]";
	}
}

Step 2.2: Create sub-class PrivilegedCustomer extending super-class Customer and also implementing java.io.Serializable interface

  • For any class said to be serializable, then it must implement java.io.Serializable interface
  • Here, sub-class PrivilegedCustomer implement java.io.Serializable interface explicitly and also extends super-class Customer
  • If any class doesn’t implements serializable interface, then NotSerializableException will be thrown at run time, although program compiles successfully
  • Overrides toString() method to print values in desired format

PrivilegedCustomer.java

package in.bench.resources.serialization.inheritance;

import java.io.Serializable;

class PrivilegedCustomer extends Customer implements Serializable {

	// instance variables
	float discountRate;
	int bonusPoints;

	@Override
	public String toString() {
		return "PrivilegedCustomer [customerId=" + customerId
				+ ", customerName=" + customerName
				+ ", discountRate=" + discountRate
				+ ", bonusPoints=" + bonusPoints + "]";
	}
}

As we are ready with POJOs implementing java.io.Serializable, we will begin with our serialization and de-serialization process from main class

Step 2.3: Serialization and De-Serialization (with Inheritance)

  • The previous case is very simple like any independent class to serialize in Java
  • But this case is bit different with respect to serialization and de-serialization process

Serialization process:

  • While serializing sub-class, JVM will check if there are any super-class which isn’t implementing java.io.Serializable interface
  • Then, inheriting instance variables of non-serializable super-class will be stored to default value ignoring their original values
  • Like 0 for Integer, null for String, etc

De-Serialization process:

  • While de-serializing sub class, JVM will check if there are any non-serializable super-class
  • Then, it will execute instance initialization flow (i.e.; similar to object instantiation flow)
  • 1st check: if there are direct initialization at instance variable declaration
  • 2nd check: if there are any initialization block for instance variable assignment
  • 3rd check: invokes no-argument constructor and looks for instance variable assignment
  • To execute 3rd check, non-serializable super-class requires no-argument constructor
  • Exception: otherwise InvalidClassException will be thrown
  • Note: For any other case, constructor is not invoked with only exception being for non-serializable super-class
  • Serializing & De-Serializing sub-class PrivilegedCustomer

SerializationWithInheritance.java

package in.bench.resources.serialization.inheritance;

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

public class SerializationWithInheritance {

	public static void main(String[] args) {

		// creating Privileged Customer object
		PrivilegedCustomer serializePrivilegedCustomer =
				new PrivilegedCustomer();

		// initialize values for privileged customer object
		serializePrivilegedCustomer.customerId = 101;
		serializePrivilegedCustomer.customerName = "SJ";
		serializePrivilegedCustomer.discountRate = 12.5f;
		serializePrivilegedCustomer.bonusPoints = 1000;

		// time to play with Serialization and De-Serialization process

		// creating output stream variables
		FileOutputStream fos = null;
		ObjectOutputStream oos = null;

		// creating input stream variables
		FileInputStream fis = null;
		ObjectInputStream ois = null;

		// creating customer object reference
		// to hold values after de-serialization
		Customer deSerializePrivilegedCustomer = null;

		try {
			// for writing or saving binary data
			fos = new FileOutputStream("CustomerInheritance.ser");

			// converting java-object to binary-format
			oos = new ObjectOutputStream(fos);

			// writing or saving customer object's value to stream
			oos.writeObject(serializePrivilegedCustomer);
			oos.flush();
			oos.close();

			System.out.println("Serialization: Privileged Customer "
					+ "object saved to CustomerInheritance.ser file\n");

			// reading binary data
			fis = new FileInputStream("CustomerInheritance.ser");

			// converting binary-data to java-object
			ois = new ObjectInputStream(fis);

			// reading object's value and casting to Customer class
			deSerializePrivilegedCustomer =
					(PrivilegedCustomer) ois.readObject();
			ois.close();

			System.out.println("De-Serialization: "
					+ "Privileged Customer object de-serialized "
					+ "from CustomerInheritance.ser file\n");
		}
		catch (FileNotFoundException fnfex) {
			fnfex.printStackTrace();
		}
		catch (IOException ioex) {
			ioex.printStackTrace();
		}
		catch (ClassNotFoundException ccex) {
			ccex.printStackTrace();
		}

		// printing customer object to console using toString() method
		System.out.println("Printing privilege customer values "
				+ "from de-serialized object... \n"
				+ deSerializePrivilegedCustomer);
	}
}

Output:

Serialization: Privileged Customer object saved to
CustomerInheritance.ser file

De-Serialization: Privileged Customer object de-serialized
from CustomerInheritance.ser file

Printing privilege customer values from de-serialized object...
PrivilegedCustomer [customerId=0, customerName=null,
discountRate=12.5, bonusPoints=1000]

3. Important points to remember while Serialization with Inheritance:

  • If super-class implements java.io.Serializable interface, then all sub-class is also serializable by default
  • It is possible to serialize sub-class, even if its corresponding super-class doesn’t implements java.io.Serializable interface
  • While serializing sub-class whose super-class doesn’t implements java.io.Serializable interface, then during serialization process inheriting instance variables of non-serializable super-class will be stored to default value ignoring their original values (like 0 for Integer, null for String, etc)
  • During de-serialization process, JVM will execute instance initialization flow in 3 steps i.e.;
    1. 1st checks direct variable assignment,
    2. 2nd check inside initialization block and then
    3. finally 3rd check inside no-argument constructor
  • For 3rd check, it is very must to code no-argument constructor inside non-serializable super-class
  • Otherwise, InvalidClassException will be thrown at run time

Related Articles:

References:

Happy Coding !!
Happy Learning !!

Java - Externalizable interface with example
Java - Serialization with Aggregation