Introduction:

Serialization: The process of writing state of an object to a file is called serialization but strictly speaking it is the process of converting an object form or java supported form into a file supported form or network supported form.
By using FileOutputStream and ObjectOutputStream classes we can implement serialization.

Serialization Diagram

Deserialization: The process of reading state of an object from the file is called deserialization but strictly speaking it is the process of converting an object from either file supported form or network supported form into java supported form.
By using FileInputStream and ObjectInputStream classes we can implement deserialization.

Deserialization Diagram
import java.io.*;
class Dog implements Serializable {
    int i = 10;
    int j = 20;
}
class SerializeDemo{
    public static void main(String[] argsthrows Exception{
         Dog d1 = new Dog();

        //serialization
        FileOutputStream fos = new FileOutputStream("abc.ser");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(d1);

        //deserialization
        FileInputStream fis = new FileInputStream("abc.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        Dog d2 = (Dog)ois.readObject();
        System.out.println("i is " + d2.i + " and j is " + d2.j);
    }
}

Output:
i is 10 and j is 20

Output
  • we can serialize only Serializable objects.
  • An Object is to be serializable if and only if the corresponding class implements Serializable interface.
  • Serializable interface present in java.io package and it does not contain any methods. It is marker interface.
  • If we are trying to serialize a non-serializable object then we will get runtime exception saying NotSerializableException.

transient keyword:

  • transient modifier applicable only for variables but not for methods and classes.
  • At the time of serialization if we don’t want to save the value of particular variable to meet security constraints then we should declare that variable as transient.
  • while performing serialization JVM ignores the original value of transient variable and save default value to the file.
  • Hence transient means not to serialize.

transient versus static:

static variable is not part of object state and hence it won’t participate in serialization. Due to this declaring static variable as transient, there is no use.


final versus transient:

final variables will be participated in the serialization directly by the value. Hence declaring a final variable as transient there is no impact.

import java.io.*;
class Dog implements Serializable {
    int i = 10;
    static transient int j = 20;
    final transient int k = 30;
}
class SerializeDemo{
    public static void main(String[] argsthrows IOExceptionClassNotFoundException{
        Dog d1 = new Dog();
        
        //serialization
        FileOutputStream fos = new FileOutputStream("abc.ser");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(d1);
        
        //deserialization
        FileInputStream fis = new FileInputStream("abc.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        Dog d2 = (Dog)ois.readObject();
        System.out.println("i is " + d2.i + " and j is " + d2.j + " and k is " + d2.k);
    }
}

Declaration Output
int i = 10;
int j = 20;
i is 10 and j = 20
transient int i = 10;
int j = 20;
i is 0 and j = 20
transient static int i = 10;
transient int j = 20;
i is 10 and j = 0
transient int i = 10;
transient final int j = 20;
i is 0 and j = 20
transient static int i = 10;
transient final int j = 20;
i is 10 and j = 20

Note:We can serialize any no of objects to the file but which order we are serialized in the same order only we have to deserialized i.e., order of objects is important in serialization.


For Example:

Dog d1 = new Dog(); 
Cat c1 = new Cat(); 
Rat r1 = new Rat();

FileOutputStream fos = new FileOutputStream(abc.ser); 
ObjectOutputStream = new ObjectOutputStream(fos); 
oos.writeObject(d1);
oos.writeObject(c1);
oos.writeObject(r1):

FileInputStream fis = new FileInputStream(abc.ser); 
ObjectInputStream = new ObjectIntputStream(fis); 
Dog d1 = (Dog)ois.readObject();
Cat c1 = (Cat)ois.readObject(); 
Rat r1 = (Rat)ois.readObject();

If we don’t know the order of objects in serialization??

FileInputStream fis = new FileInputStream(abc.ser); 
ObjectInputStream = new ObjectIntputStream(fis); 
Object o = ois.readObject();
if(o instanceof Dog){
     Dog d2 = (Dog)o;
     //perform Dog specific functionalities
}
else if(o instanceof Cat){
     Cat c2 = (Cat)o;
     //perform Cat specific functionalities
}
else if(o instanceof Rat){
     Rat r2 = (Rat)o;
     //Perform Rat specific functionalities
}

Object Graphs in serialization:

Whenever we are serializing an object then the set of all objects which are reachable from that object will be serialized automatically. this group of objects is nothing but object graph.
in object graph every object should be serializable. if at least one object is not serializable then 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 SerializeDemo{
    public static void main(String[] argsthrows IOExceptionClassNotFoundException{
        Dog d1 = new Dog();

        //serialization
        FileOutputStream fos = new FileOutputStream("abc.ser");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(d1);

        //deserialization
        FileInputStream fis = new FileInputStream("abc.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        Dog d2 = (Dog)ois.readObject();

        System.out.println("j is " + d2.c.r.j);
    }
}

Output:
j is 20

output

In the above program whenever e=we are serializing Dog object automatically Cat and rat objects got serialized because these are part of object Graph of Dog.
Among Dog, Cat and Rat Objects if at least one object is not serializable then we will get runtime exception saying NotSerializableException.


Customized serialization:

During default serialization they may be chance of loss of information because of transient keyword.

import java.io.*;
class Account implements Serializable{
    String username = "kalyan";
    transient String password = "pwd@1234";
}
class CustomSerializeDemo{
    public static void main(String[] argsthrows IOExceptionClassNotFoundException{
        Account a1 = new Account();
        //serialization 
        System.out.println("Username is " + a1.username + " and Password is " + a1.password);
        FileOutputStream fos = new FileOutputStream("abc.ser");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(a1);
        
        //deserialization
        FileInputStream fis = new FileInputStream("abc.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        Account a2 = (Account)ois.readObject();
        System.out.println("Username is " + a2.username + " and Password is " + a2.password);
    }
}

Output:
Username is kalyan and password is pwd@1234
Username is kalyan and password is null


Output

In the above example before serialization Account object can provide proper username and password but after deserialization Account object can provide only username but not password. This is due to declaring password variable as transient. hence during keep on serialization there may be a chance loss of information because of transient keyword.
To recover this loss of information we should go for customized serialization.

we can implement customized serialization by using the following two methods.


private void writeObject(ObjectOutputStream os) throws Exception

This method will be executed automatically at the time of serialization.
Hence at the time of serialization if we want to perform any activity we have to define that in this method only.


private void readObject(ObjectInputStream is) throws Exception

This method will be executed automatically at the time of deserialization.
Hence at the time of deserialization if we want to perform any activity, we have to define that in this method only.

Note: The above methods are callback methods because these are executed automatically by the JVM.

While performing which object serialization we have to do extra work in the corresponding class we have to define above methods.
For example, while performing Account object serialization if we require to do extra work, in that Account class we have to define above methods


import java.io.*;
class Account implements Serializable{
    String username = "kalyan";
    transient String password = "pwd@1234";
    private void writeObject(ObjectOutputStream osthrows IOException{
        os.defaultWriteObject();
        String encryptPassword = "123" + password;
        os.writeObject(encryptPassword);
    }
    private void readObject(ObjectInputStream isthrows ClassNotFoundExceptionIOException{
        is.defaultReadObject();
        String decryptPassword = (String)is.readObject();
        password = decryptPassword.substring(3);
    }
}
class CustomSerializeDemo{
    public static void main(String[] argsthrows IOExceptionClassNotFoundException{
        Account a1 = new Account();
        //serialization
        System.out.println("Username is " + a1.username + "and Password is " + a1.password);
        FileOutputStream fos = new FileOutputStream("abc.ser");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(a1);
        //deserialization
        FileInputStream fis = new FileInputStream("abc.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        Account a2 = (Account)ois.readObject();
        System.out.println("Username is " + a2.username + "and Password is " + a2.password);
    }
}

Output:
Username is kalyan and password is pwd@1234
Username is kalyan and password is pwd@1234


Output

In the above program before serialization and after serialization Account object can provide proper username and password.

Note: programmer can’t call private methods directly from outside of the class. JVM can call private methods directly from outside of the class.


import java.io.*;class Account implements Serializable{
    String username = "kalyan";
    transient String password = "pwd@1234";
    transient int pin = 1234;
    private void writeObject(ObjectOutputStream osthrows IOException{
        osdefaultWriteObject();
        String encryptPassword = "123" + password;
        int encryptPin = 3333 + pin;
        os.writeObject(encryptPassword);
        os.writeInt(encryptPin);
    }
    private void readObject(ObjectInputStream isthrows IOExceptionClassNotFoundException{
        is.defaultReadObject();
        String decryptPassword = (String)is.readObject();
        password = decryptPassword.substring(3);
        int decryptPin = is.readInt();
        pin = decryptPin - 3333;
    }
}
class CustomSerializeDemo{
    public static void main(String[] argsthrows IOExceptionClassNotFoundException{
        Account a1 = new Account();

        //serialization
        System.out.println("Username is " + a1.username + " and Password is " + a1.password + " and Pin is " + a1.pin);
        FileOutputStream fos = new FileOutputStream("abc.ser");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(a1);

        //deserialization
        FileInputStream fis = new FileInputStream("abc.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        Account a2 = (Account)ois.readObject();
        System.out.println("Username is " + a2.username + " and Password is " + a2.password + " and Pin is " + a2.pin);
    }
}

Output:
Username is kalyan and password is pwd@1234 and pin is 1234
Username is kalyan and password is pwd@1234 and pin is 1234


serialization w.r.to inheritance:


case: 1
Even though child class doesn’t implement Serializable we can serialize child class object if parent class implements Serializable interface. i.e., serializable nature is inheriting from parent to child.
hence if parent is Serializable then every child is serializable.


import java.io.*;
class Animal implements Serializable{
    int i = 10;
}
class Dog extends Animal{
    int j = 20;
}
class SerializeDemo{
    public static void main(String[] argsthrows IOExceptionClassNotFoundException{
        Dog d1 = new Dog();
        System.out.println("i is " + d1.i + "j is " + d1.j);
        //serialization
        FileOutputStream fos = new FileOutputStream("abc.ser");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(d1);

        //deserialization
        FileInputStream fis = new FileInputStream("abc.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        Dog d2 = (Dog)ois.readObject();
        System.out.println("i is " + d2.i + "j is " + d2.j);
    }
}

In the above example even though Dog class doesn’t implement Serializable we can serialize Dog object because its parent Animal class implements Serializable.

Note: Object class doesn’t implement serializable interface.

Case: 2

Even though parent class doesn’t implement serializable we can serialize child class object if child class implements serializable interface i.e., to serialize child class object parent class need not be serializable.
At the time of serialization JVM will check is any variable inheriting from non-serializable parent or not . if any variable inheriting from non-serializable parent, then JVM ignores original value and save default value to the file.
At the time of deserialization JVM will check is any parent class is non serializable or not. if any parent class is non serializable then JVM will execute instance control flow in every non serializable parent and share its instance variable to the current object.
while executing instance control flow of non-serialize parent JVM will always call no-args constructor. hence every non serializable class should compulsorily contain no-args constructor. It may be default constructor generated by compiler or customised constructor explicitly provided by programmer.
Otherwise, we will get runtime exception saying 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 SerializeDemo{
    public static void main(String[] args) throws IOException, ClassNotFoundException{
        Dog d1 = new Dog();
        d1.i = 888;
        d1.j = 999;
        System.out.println("i is " + d1.i + "j is " + d1.j);
        //serialization
        FileOutputStream fos = new FileOutputStream("abc.ser");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(d1);

        //deserialization
        System.out.println("deserialization* started");
        FileInputStream fis = new FileInputStream("abc.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        Dog d2 = (Dog)ois.readObject();
        System.out.println("i is " + d2.i + "j is " + d2.j);
    }
}

Output

For Example InvalidClassException [args constructor]


import java.io.*;class Animal{
    int i = 10;
    Animal(int i ){
        System.out.println("Animal constructor called");
    }
}
class Dog extends Animal implements serializable{
    int j = 20;
    Dog(){
        super(10);
        System.out.println("Dog constructor called");
    }
}
class SerializeDemo{
    public static void main(String[] args){
        Dog d1 = new Dog();
        d1.i = 888;
        d1.j = 999;
        System.out.println("i is " + d1.i + "j is " + d1.j);
        //serialization
        FileOutputStream fos = new FileOutputStream("abc.ser");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(d1);

        //deserialization
        System.out.println("deserialization started");
        FileInputStream fis = new FileInputStream("abc.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        Dog d2 = (Dog)ois.readObject(); //InvalidClassException
        System.out.println("i is " + d2.i + "j is " + d2.j);
    }
}

Output:
InvalidClassException



Externalization:

In serialization everything takes care by JVM and programmer doesn’t have any control.
In serialization it is always possible to save total object to the file and it is not possible to save part of the object, which may create performance problems.

To overcome this problem, we should go for externalization.
The main advantage of externalization over serialization is everything takes care by programmer and JVM doesn’t have any control.
Based on our requirement we can either save total object or part of the object which improves performance of the system.
To provide Externalizable ability for any java object compulsory the corresponding class should implement Externalizable interface.

Externalizable interface defines two methods.

writeExternal()
readExternal()

Externalizable is the child interface of Serializable
Serializable(I) (1.1 Version)
Externalizable(I) (1.1 Version)


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 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 read required variables from the file and assign to current object.
For strictly speaking at the time of deserialization JVM will create a separate new object by executing public no-args constructor.
On that object JVM will call readExternal() method.
Hence every Externalizable implemented class should compulsorily contain public no-args constructor otherwise we will get runtime exception saying InvalidClassException.


import java.io.*;
class MyClass implements Externalizable{
    String s;
    int ij;
    public MyClass(){
        System.out.println("public no-args Constructor");
    }
    MyClass(String sint iint j){
        this.s = s;
        this.i = i;
        this.j = j;
    }
    public void writeExternal(ObjectOutput outthrows IOException{
        out.writeObject(s);
        out.writeInt(i);
    }
    public void readExternal(ObjectInput inthrows IOExceptionClassNotFoundException{
        s = (String)in.readObject();
        i = in.readInt();
    }
}
class ExternalizableDemo{
    public static void main(String[] argsthrows IOExceptionClassNotFoundException{
        MyClass m1 = new MyClass("kalyan"1020);
        FileOutputStream fos = new FileOutputStream("abc.ser");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(m1);
        FileInputStream fis = new FileInputStream("abc.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        MyClass m2 = (MyClass)ois.readObject();
        System.out.println(m2.s + " " + m2.i + "    " + m2.j);
    }
}

If the class implements Serializable then total object will be saved to the file.
In this case output is
Kalyan 10 20

If the class implements Externalizable then only required variables will be saved to the file.
In this case output is public no-args constructor
kalyan 10 0

Note: In serialization transient keyword will play role but in externalization transient keyword wont play any role of course transient keyword not required in externalization.


Differences between Serialization and Externalization:

Serialization Externalization
It is meant for Customized Serialization It is meant for Customized Serialization
Here everything takes care by JVM and programmer doesn’t have any control Here everything takes care by programmer and JVM doesn’t have any control
In this case it is always possible to save total object to the file and it is not possible to save part of the object Based on our requirement we can save either total object or part of the object
It is the best choice if we want to save total object of the file It is the best choice if we want to save part of the object of the file
Serializable interface doesn’t contain any methods and it is marker interface Externalizable interface contains two methods writeExternal() and readExtrenal() and hence it is marker interface
Serializable implemented class not required to contain public no-args constructor Externalizable implemented class should compulsory contain public no-args constructor. Otherwise, we will get runtime exception saying InvalidClassException.
transient keyword will play role in serialization transient keyword won’t play role in externalization, of course it won’t be required.

serialVersionUID:

In serialization both sender and receiver need not be same person, need not use same machine and need not be from same location.
The persons may be different, the machines may be different and locations may be different.

In serialization both sender and receiver should have .class file at beginning only just state of object is travelling from sender to receiver.

At the time of serialization with every object sender side JVM will save a unique identifier.
JVM is responsible to generate this unique identifier based on .class file.
At the time of deserialization receiver side JVM will compare unique identifier associate with object with local class unique identifier. If both are matched then only deserialization will be performed otherwise, we will get runtime exception saying InvalidClassException

This unique identifier is nothing but serialVersionUID.
Problems of depending on default serialVersionUID generated by JVM:

1. Both sender and receiver should use same JVM with respect to vendor and platform and version Otherwise receiver unable to deserialize because of different serialVersionUIDs.

2. Both sender and receiver should use same .class file version. After serialization if there is any change in .class file at receiver side then receiver unable to deserialize.

3. To generate serialVersionUID internally JVM may use complex algorithm which may create performance problems.

4. we can solve above problems by configuring our own serialVersionUID.
we can configure our own serialVersionUID as follows

private static final long serialVersionUID = 1L;


import java.io.*;
class Dog implements Serializable{
    private static final long serialVersionUID = 1L;
    int i = 10;
    int j = 20;
}
class Sender{
    public static void main(String[] argsthrows 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[] argsthrows Exception{
        FileInputStream fis = new FileInputStream("abc.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        Dog d2 = (Dog)ois.readObject();
        System.out.println("i is " + d2.i + " and j is " + d2.j);
    }
}

Output:
i is 10 and j is 20

In the above program after serialization if we perform any change to the .class file a receiver side we won’t get any problem at the time of deserialization.

In this case sender and receiver not required to maintain same JVM versions.

Note:
some IDEs prompt programmer to enter serialVersionUID explicitly.
Some IDEs may generate serialVersionUID automatically.