Java – Serialization with Aggregation

In this article, we will discuss Serialization with Aggregation i.e.; serializing class contains reference to other classes. It forms a HAS-A relationship

There are 2 scenarios with respect to HAS-A relationship

  1. All reference classes/objects inside a serializing class/object is serializable
  2. One or some of the reference classes/objects inside a serializing class/object is NOT serializable

Here, serializing class must implement java.io.Serializable

1. Serialization process:

  • During serialization process i.e.; saving state of an Object to File, only instance variables will be participated and persisted to file storage or some other storage via network capability

2. De-Serialization process:

  • During de-serialization process, Object’s state will be restored back from file storage to java heap memory

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

3. Serialization with Aggregation

Step 1: Create 2 POJO classes for Address and Phone –> implementing java.io.Serializable interface

  • For any class said to be serializable, if it implement java.io.Serializable interface
  • Otherwise, NotSerializableException will be thrown at run time, although program compiles successfully
  • Both Address & Phone POJO has 2-arg parameterized constructor
  • Overrides toString() method to print values in desired format

Address.java

package in.bench.resources.serialization.aggregation;

import java.io.Serializable;

class Address implements Serializable {

	// instance variables
	int flatNo;
	String streetName;

	// 2-arg parameterized constructor
	public Address(int flatNo, String streetName) {
		super();
		this.flatNo = flatNo;
		this.streetName = streetName;
	}

	// overriding toString() method
	@Override
	public String toString() {
		return "Address ["
				+ "flatNo=" + flatNo
				+ ", streetName=" + streetName
				+ "]";
	}
}

Phone.java

package in.bench.resources.serialization.aggregation;

import java.io.Serializable;

class Phone implements Serializable {

	// instance variables
	int countryCode;
	int telephoneNumber;

	// 2-arg parameterized constructor
	public Phone(int countryCode, int telephoneNumber) {
		super();
		this.countryCode = countryCode;
		this.telephoneNumber = telephoneNumber;
	}

	// overriding toString() method
	@Override
	public String toString() {
		return "Phone ["
				+ "countryCode=" + countryCode
				+ ", telephoneNumber=" + telephoneNumber
				+ "]";
	}
}

Step 2: Create another POJO class called Customer which will have reference to both Address and Phone classes

  • That is, Customer class aggregates both Address and Phone classes (HAS-A relationship)
  • For any class said to be serializable, if it implement java.io.Serializable interface
  • Otherwise, NotSerializableException will be thrown at run time, although program compiles successfully
  • Customer POJO has 4-arg parameterized constructor which includes both Address and Phone classes
  • Overrides toString() method to print values in desired format

Customer.java

package in.bench.resources.serialization.aggregation;

import java.io.Serializable;

class Customer implements Serializable {

	// instance variables
	int customerId;
	String customerName;
	Address address;
	Phone phone;

	// 4-arg parameterized constructor
	public Customer(int customerId, String customerName,
			Address address, Phone phone) {
		super();
		this.customerId = customerId;
		this.customerName = customerName;
		this.address = address;
		this.phone = phone;
	}

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

As we are ready with POJOs, we will begin with our serialization and de-serialization process from main class

Step 3: Serialization and De-Serialization (with Aggregation)

  • To Serialize any Object, we can use ObjectOutputStream & FileOutputStream to write/save to the 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

3.1 When all reference classes/objects inside Customer class is serializable

  • Here, both aggregating classes Address and Phone is serializable
  • Also main Customer class which has reference to Address/Phone is also serializable

SerializationWithAggregation.java

package in.bench.resources.serialization.aggregation;

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

	public static void main(String[] args) {

		// creating address object --> implements java.io.Serializable
		Address address = new Address(402, "2nd street");

		// creating phone object --> implements java.io.Serializable
		Phone phone = new Phone(022, 27759868);

		// creating customer object --> implements java.io.Serializable
		Customer serializeCustomer =
				new Customer(101, "SJ", address, phone);

		// 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 deSerializeCustomer = null;

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

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

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

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

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

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

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

			System.out.println("De-Serialization: Customer object "
					+ "de-serialized from CustomerAggregation.ser file");
		}
		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 customer values from "
				+ "de-serialized object... \n" + deSerializeCustomer);
	}
}

Output:

Serialization: Customer object saved to CustomerAggregation.ser file

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

Printing customer values from de-serialized object...
Customer [customerId=101, customerName=SJ,
address=Address [flatNo=402, streetName=2nd street],
phone=Phone [countryCode=18, telephoneNumber=27759868]]

3.2 When one or some of the reference classes/objects inside Customer class is NOT serializable

  • For demo purpose we will remove “implements Serializable” from Address class
  • Exception: All classes inside Customer class should be serializable
  • Otherwise, NotSerializableException will be thrown at run time, although program compiles successfully
  • Here, Address class doesn’t implement java.io.Serializable interface

Address.java

package in.bench.resources.serialization.aggregation;

class Address {

	// instance variables
	int flatNo;
	String streetName;

	// 2-arg parameterized constructor
	public Address(int flatNo, String streetName) {
		super();
		this.flatNo = flatNo;
		this.streetName = streetName;
	}

	// overriding toString() method
	@Override
	public String toString() {
		return "Address [flatNo=" + flatNo
				+ ", streetName=" + streetName + "]";
	}
}

Note: This program is very same, as that of program 1 or case 1

SerializationWithAggregation.java

package in.bench.resources.serialization.aggregation;

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

	public static void main(String[] args) {

		// creating address object --> implements java.io.Serializable
		Address address = new Address(402, "2nd street");

		// creating phone object --> implements java.io.Serializable
		Phone phone = new Phone(022, 27759868);

		// creating customer object --> implements java.io.Serializable
		Customer serializeCustomer =
				new Customer(101, "SJ", address, phone);

		// 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 deSerializeCustomer = null;

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

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

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

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

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

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

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

			System.out.println("De-Serialization: Customer object "
					+ "de-serialized from CustomerAggregation.ser file");
		}
		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 customer values from "
				+ "de-serialized object... \n" + deSerializeCustomer);
	}
}

Output:

java.io.NotSerializableException: in.bench.resources.serialization
.aggregation.Address
	at java.io.ObjectOutputStream.writeObject0(Unknown Source)
	at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source)
	at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
	at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
	at java.io.ObjectOutputStream.writeObject0(Unknown Source)
	at java.io.ObjectOutputStream.writeObject(Unknown Source)
	at in.bench.resources.serialization.aggregation
.SerializationWithAggregation.main(SerializationWithAggregation.java:110)
Printing customer values from de-serialized object...
null

Explanation:

  • JVM throws NotSerializableException for Address class, while serializing Customer class
  • So, it’s very must for every class inside Serializing class to implement java.io.Serializable

Case-study:

  • try for Phone class by removing implements Serializable
  • but before that rectify above exception by implementing serializable for Address class

4. Important points to remember while Serialization with Aggregation classes:

  • Rule 1: all classes that need to be serialized must implement java.io.Serializable interface
  • Rule 2: All reference classes inside a serializable class must also be java.io.Serializable
  • Rule 3: If any of the class is not implementing java.io.Serializable in the serialization process, then JVM will throw NotSerializableException

Related Articles:

References:

Happy Coding !!
Happy Learning !!

Java - Serialization with Inheritance
Java - Order of Serialization and De-Serialization