Serialization in Java


  • The process of saving (or) writing state of an object to a file is called serialization
  • It is the process of converting an object from java supported form to either network supported form (or) file supported form.
  • By using FileOutputStream and ObjectOutputStream classes we can achieve serialization process

Need of Serialization in java?

Serialization is usually used when there is a need to send your data over the network or to store in files. By data, I mean objects and not text. Now the problem is your network infrastructure and your Hard disk are hardware components that understand bits and bytes but not Java objects. Serialization is the translation of Java object’s values/states to bytes to send it over a network or to save it. On the other hand, Deserialization is a conversion of byte code to corresponding java objects.

For Serialization in java


Employee.java

package com.bala.serilizaion;
import java.io.Serializable;

public class Employee implements Serializable{ 
   private static final long serialVersionUID = 1L;
   
    int employeeId;
    String employeeName;
    String department;
    
    public int getEmployeeId() {
        return employeeId;
    }
    public void setEmployeeId(int employeeId) {
        this.employeeId = employeeId;
    }
    public String getEmployeeName() {
        return employeeName;
    }
    public void setEmployeeName(String employeeName) {
        this.employeeName = employeeName;
    }
    public String getDepartment() {
        return department;
    }
    public void setDepartment(String department) {
        this.department = department;
    }
}

As you can see above, if you want to serialize any class then it must implement the Serializable interface which is a marker interface.
Marker interface in Java is interfaces with no field or methods or in simple word empty interface in java is called marker interface

Create SerializeMain.java

package com.bala.serilizaion;
import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; public class SerializeMain { public static void main(String[] args) { Employee emp = new Employee(); emp.setEmployeeId(100); emp.setEmployeeName("Bala"); emp.setDepartment("CS"); try { FileOutputStream fileOut = new FileOutputStream("employee.ser"); ObjectOutputStream outStream = new ObjectOutputStream(fileOut); outStream.writeObject(emp); outStream.close(); fileOut.close(); }catch(IOException i) { i.printStackTrace(); } } }

For Deserialization

DeserializeMain.java

package com.bala.serilizaion;
import java.io.IOException; import java.io.ObjectInputStream; public class DeserializeMain { public static void main(String[] args) { Employee emp = null; try { FileInputStream fileIn =new FileInputStream("employee.ser"); ObjectInputStream in = new ObjectInputStream(fileIn); emp = (Employee) in.readObject(); in.close(); fileIn.close(); }catch(IOException i) { i.printStackTrace(); return; }catch(ClassNotFoundException c) { System.out.println("Employee class not found"); c.printStackTrace(); return; } System.out.println("Deserialized Employee..."); System.out.println("Emp id: " + emp.getEmployeeId()); System.out.println("Name: " + emp.getEmployeeName()); System.out.println("Department: " + emp.getDepartment()); } } . . .
Output:
Deserialized Employee... Emp id: 100 Name: Bala Department: CS

Transient keyword

1) transient is the modifier applicable only for variables. 

2) While performing serialization if we don't want to save the value of a particular variable to meet security constant such type of variable, then we should declare that variable with a "transient" keyword.

3) At the time of serialization, JVM ignores the original value of the transient variable and save the default value to the file. 4) That is transient means "not to serialize".

Static Vs Transient

a static variable is not part of object state hence they won't participate in serialization because of this declaring a static variable as transient there is no use.

Transient Vs Final

final variables will be participated in serialization directly by their values. Hence declaring a final variable as transient there is no use. The compiler assigns the value to final variable

import java.io.*;
class Dog implements Serializable { static transient int i=10; final transient int j=20; } class SerializableDemo { public static void main(String args[])throws Exception{ Dog d1=new Dog(); FileOutputStream fos=new FileOutputStream("abc.ser"); ObjectOutputStream oos=new ObjectOutputStream(fos); oos.writeObject(d1); FileInputStream fis=new FileInputStream("abc.ser"); ObjectInputStream ois=new ObjectInputStream(fis); Dog d2=(Dog)ois.readObject(); System.out.println(d2.i+"................"+d2.j); } } Output: 10................20


declaration output int i=10; int j=20; 10................20 transient int i=10; int j=20; 0................20 transient int i=10; transient static int j=20; 0................20 transient final int i=10; transient int j=20; 10................0 transient final int i=10; transient static int j=20; 1

We can serialize any no of objects to the file but in which order we serialized in the same order only we have to deserialize.

Dog d1=new Dog( );
Cat c1=new Cat( ); Rat r1=new Rat( ); FileOutputStream fos=new FileOutputStream("abc.ser"); ObjectOutputStream oos=new ObjectOutputStream(fos); oos.writeObject(d1); oos.writeObject(c1); oos.writeObject(r1); FileInputStream fis=new FileInputStream("abc.ser"); ObjectInputStream ois=new ObjectInputStream(fis); Dog d2=(Dog)ois.readObject(); Cat c2=(Cat)ois.readObject(); Rat r2=(Rat)ois.readObject(); . . .

If we don't know the order of objects :

FileInputStream fis=new FileInputStream("abc.ser");
ObjectInputStream ois=new ObjectInputStream(fis); Object o=ois.readObject( ); if(o instanceof Dog) { Dog d2=(Dog)o; //perform Dog specific functionality } else if(o instanceof Cat) { Cat c2=(Cat)o; //perform Cat specific functionality } . . . }

Object graph in serialization

1) Whenever we are serializing an object the set of all objects which are reachable from that object will be serialized automatically. This group of objects is nothing but the object graph in serialization. 

 2) In object graph every object should be Serializable otherwise we will get runtime exception saying"NotSerializableException".

import java.io.*;
class Dog implements Serializable { Cat c=new Cat(); } class Cat implements Serializable { Rat r=new Rat(); } class Rat implements Serializable { int j=20; } class SerializableDemo { public static void main(String args[])throws Exception{ Dog d1=new Dog(); FileOutputStream fos=new FileOutputStream("abc.ser"); ObjectOutputStream oos=new ObjectOutputStream(fos); oos.writeObject(d1); FileInputStream fis=new FileInputStream("abc.ser"); ObjectInputStream ois=new ObjectInputStream(fis); Dog d2=(Dog)ois.readObject(); System.out.println(d2.c.r.j); } } Output: 20


  • In the above example whenever we are serializing Dog object automatically Cat and Rat objects will be serialized because these are part of the object graph of Dog object.
  • Among Dog, Cat, Rat if at least one object is not serializable then we will get runtime exception saying "NotSerializableException".

Customized serialization

During default Serialization, there may be a chance of loss of information due to the transient keyword. (Ex: mango, money, box)

import java.io.*;
class Account implements Serializable { String userName="Bala"; transient String pwd="Anand"; } class CustomizedSerializeDemo { public static void main(String[] args)throws Exception{ Account a1=new Account(); System.out.println(a1.userName+"........."+a1.pwd); FileOutputStream fos=new FileOutputStream("abc.ser"); ObjectOutputStream oos=new ObjectOutputStream(fos); oos.writeObject(a1); FileInputStream fis=new FileInputStream("abc.ser"); ObjectInputStream ois=new ObjectInputStream(fis); Account a2=(Account)ois.readObject(); System.out.println(a2.userName+"........."+a2.pwd); } } Output: Bala.........Anand Bala.........null

rdiagram1

At the time of Account object serialization, JVM will check is there any writeObject() method in Account class or not. If it is not available then JVM is responsible to perform serialization(default serialization). If Account class contains writeObject() method then JVM feels very happy and executes that Account class writeObject() method. The same rule is applicable for the readObject() method also.

Serialization with respect to inheritance

Case 1: 

If parent class implements Serializable then automatically every child class by default implements Serializable. That is Serializable nature is inheriting from parent to child. Hence even though child class doesn't implements Serializable, we can serialize child class objects if parent class implements a Serializable interface.

import java.io.*;
class Animal implements Serializable { int i=10; } class Dog extends Animal { int j=20; } class SerializableWRTInheritance { public static void main(String[] args)throws Exception{ Dog d1=new Dog(); System.out.println(d1.i+"........"+d1.j); FileOutputStream fos=new FileOutputStream("abc.ser"); ObjectOutputStream oos=new ObjectOutputStream(fos); oos.writeObject(d1); FileInputStream fis=new FileInputStream("abc.ser"); ObjectInputStream ois=new ObjectInputStream(fis); Dog d2=(Dog)ois.readObject(); System.out.println(d2.i+"........"+d2.j); } } Output: 10........20 10........20

Even though Dog class does not implements Serializable interface explicitly but we can Serialize Dog object because its parent class animal already implements Serializable interface. 

 Note: Object class doesn't implement the Serializable interface.

Case 2:

  • Even though parent class does not implements Serializable we can serialize child object if child class implements Serializable interface.
  • At the time of serialization, JVM ignores the values of instance variables that are coming from non Serializable parent then instead of the original value, JVM saves default values for those variables to the file.
  • At the time of Deserialization JVM checks whether any parent class is non Serializable or not. If any parent class is non Serializable JVM creates a separate object for every non Serializable parent and shares its instance variables to the current object.
  • To create an object for non-serializable parent JVM always calls no arg constructor(default constructor) of that non Serializable parent hence every non Serializable parent should compulsory contain no arg constructor otherwise we will get runtime exception "InvalidClassException" .
  • To create an object for non-serializable parent JVM always calls no-arg constructor(default constructor) of that non-serializable parent hence every non-serializable parent should compulsorily contain no-arg constructor otherwise we will get runtime exception "InvalidClassException".
import java.io.*;
class Animal { int i=10; Animal(){ System.out.println("Animal constructor called"); } } class Dog extends Animal implements Serializable { int j=20; Dog(){ System.out.println("Dog constructor called"); } } class SerializableWRTInheritance { public static void main(String[] args)throws Exception{ Dog d1=new Dog(); d1.i=888; d1.j=999;

FileOutputStream fos=new FileOutputStream("abc.ser");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(d1);
System.out.println("Deserialization started");
FileInputStream fis=new FileInputStream("abc.ser");
ObjectInputStream ois=new ObjectInputStream(fis);
Dog d2=(Dog)ois.readObject();
System.out.println(d2.i+"........."+d2.j);
}
}
Output:
Animal constructor called
Dog constructor called
Deserialization started
Animal constructor called
10.........999
 

Externalization : ( 1.1 v )

  • In default serialization everything takes care by JVM and programmer doesn't have any control.
  • In serialization total object will be saved always and it is not possible to save part of the object, which creates performance problems at a certain point.
  • To overcome these problems we should go for externalization where everything takes care by programmer and JVM doesn't have any control.
  • The main advantage of externalization over serialization is we can save either total object or part of the object based on our requirement.
  • To provide an Externalizable ability for any object compulsory the corresponding class should implement the Externalizable interface.
  • The Externalizable interface is the child interface of the Serializable interface.

The externalizable interface defines 2 methods 

1) writeExternal( ) 

2) readExternal( )

public void writeExternal(ObjectOutput out) throws IOException 

This method will be executed automatically at the time of Serialization within this method, we have to write code to save the required variables to the file.

public void readExternal(ObjectInput in) throws IOException , ClassNotFoundException 

This method will be executed automatically at the time of deserialization within this method, we have to write code to save read a required variable from file and assign to the current object

At the time of deserialization, Jvm will create a separate new object by executing public no-arg constructor on that object JVM will call the readExternal() method.

Every Externalizable class should compulsory contains public no-arg constructor otherwise we will get RuntimeExcepion saying "InvaidClassException".

import java.io.*;
class ExternalDemo implements Externalizable { String s ; int i ; int j ; public ExternalDemo() { System.out.println("public no-arg constructor"); } public ExternalDemo(String s , int i, int j) { this.s=s ; this.i=i ; this.j=j ; } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(s); out.writeInt(i); } public void readExternal(ObjectInput in) throws IOException , ClassNotFoundException { s=(String)in.readObject(); i= in.readInt(); } } public class Externalizable1 { public static void main(String[] args)throws Exception { ExternalDemo t1=new ExternalDemo("ashok", 10, 20); FileOutputStream fos=new FileOutputStream("abc.ser"); ObjectOutputStream oos=new ObjectOutputStream(fos); oos.writeObject(t1); FileInputStream fis=new FileInputStream("abc.ser"); ObjectInputStream ois=new ObjectInputStream(fis); ExternalDemo t2=(ExternalDemo)ois.readObject(); System.out.println(t2.s+"-------"+t2.i+"--------"+t2.j); } } output : public no-arg constructor ashok -------- 10 ------ 0

1) If the class implements the Externalizable interface then only part of the object will be saved in the case output is

public no-arg constructor
ashok ---- 10 ----- 0

2) If the class implements Serializable interface then the output is ashok --- 10 --- 20

3) In externalization transient keyword won't play any role, hence transient keyword not required.


 Serialization Externalization
 It is meant for default Serialization It is meant for Customized Serialization 
 Here everything takes care by JVM and the programmer doesn't have any control  Here everything takes care of the programmer and JVM doesn't have any control. 
 Here total object will be saved always and it is not possible to save part of the object.  Here based on our requirement we can save either total object or part of the object. 
 Serialization is the best choice if we want to save the total object to the file. Externalization is the best choice if we want to save part of the object. 
 Relatively performance is low relatively.performance is high.
 The Serializable interface doesn't contain any method, and it is a marker interface.  It is not a marker interface.
 Serializable class not required to contains a public no-arg constructor.  The externalizable class should compulsory contains public no-arg constructor otherwise we will get RuntimeException saying "InvalidClassException".
 the transient keyword play role in serialization.  the transient keyword doesn't play any role in Externalization

serialVersionUID 

To perform Serialization & Deserialization internally JVM will use a unique identifier, which is nothing but serialVersionUID. At the time of serialization, JVM will save serialVersionUID with the object. At the time of Deserialization JVM will compare serialVersionUID and if it is matched then the only object will be Deserialized otherwise we will get RuntimeException saying "InvalidClassException".

The process in depending on default serialVersionUID is

1) After the Serializing object if we change the .class file then we can't perform deserialization because of mismatch in serialVersionUID of local class and serialized object in this case at the time of Deserialization we will get RuntimeException saying in "InvalidClassException". 

2) Both sender and receiver should use the same version of JVM if there any incompatibility in JVM versions then receive unable to deserialize because of different serialVersionUID, in this case, the receiver will get RuntimeException saying "InvalidClassException". 

3) To generate serialVersionUID internally JVM will use a complex algorithm that may create performance problems. We can solve the above problems by configuring our own serialVersionUID

we can configure serialVersionUID as follows : 

private static final long serialVersionUID = 1L;

class Dog implements Serializable {
private static final long serialVersionUID=1L; int i=10; int j=20; } class Sender { public static void main(String[] args) throws Exception { Dog d1=new Dog(); FileOutputStream fos=new FileOutputStream("abc.ser"); ObjectOutputStream oos= new ObjectOutputStream(fos); oos.writeObject(d1); } } class Receiver { public static void main(String[] args)throws Exception { FileInputStream fis=new FileInputStream("abc.ser"); ObjectInputStream ois=new ObjectInputStream(fis); Dog d2=(Dog) ois.readObject(); System.out.println(d2.i+"-----"+d2.j); } }

In the above program after serialization even though if we perform any change to Dog.class file , we can deserialize the object. We if configure our own serialVersionUID both sender and receiver not required to maintain the same JVM versions.

 Note: Some IDE's generate explicit serialVersionUID.