Showing posts with label Java serialization. Show all posts
Showing posts with label Java serialization. Show all posts

Saturday, December 10, 2022

Externalizable Interface in Java

Serialization in Java provides a pretty good default implementation for serializing and deserializing an object. All you need to do is to implement Serializable interface and the whole process is automatic for you.

But, what if you want to control the process of serialization, you have some fields in your object which hold confidential information and you don’t want to serialize those fields or a sub-object with in your object graph doesn’t need to be serialized. That’s when you can use Externalizable interface in Java for custom serialization and deserialization.


Externalizable interface in Java

Externalizable interface extends the Serializable interface (which is a marker interface) and adds two methods writeExternal() and readExternal().

When you use Externalizable for your serialization, you will implement Externalizable interface and implement writeExternal() and readExternal() methods. These two methods will be called while serializing and deserializing respectively.

General form of writeExternal() and readExternal()

  • readExternal(ObjectInput in)- The object implements the readExternal method to restore its contents by calling the methods of DataInput for primitive types and readObject for objects, strings and arrays.
  • writeExternal(ObjectOutput out)- The object implements the writeExternal method to save its contents by calling the methods of DataOutput for its primitive values or calling the writeObject method of ObjectOutput for objects, strings, and arrays.

Refer Serialization in Java to see another way, using readObject and writeObject to control the process of serialization.

Serialization process when Externalizable interface is used

Each object to be stored is tested for the Externalizable interface. If the object supports Externalizable, the writeExternal method is called. If the object does not support Externalizable and does implement Serializable, the object is saved using ObjectOutputStream.

When an Externalizable object is reconstructed, an instance is created using the public no-arg constructor, then the readExternal method called. Serializable objects are restored by reading them from an ObjectInputStream.

Externalizable interface Java example

Let’s see an example where we have a class called User in which pwd field is there that you don’t want to convert into bytes. Though that can also be done by marking it as transient but here let us see how Externalizable can be used to control the serialization.

User class

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

public class User implements Externalizable {
 private String userName;
 private int id;
 private String pwd;
 
 // no-arg constructor **Required**
 public User(){
  System.out.println("In no-arg constructor");
 }
 
 public User(String userName, int id, String pwd){
  System.out.println("In constructor with args");
  this.userName = userName;
  this.id = id;
  this.pwd = pwd;
 }
 
 public String getUserName() {
  return userName;
 }

 public int getId() {
  return id;
 }

 public String getPwd() {
  return pwd;
 }

 @Override
 public void writeExternal(ObjectOutput out) throws IOException {
  System.out.println("In writeExternal method");
  out.writeObject(userName);
  out.writeInt(id);
 }

 @Override
 public void readExternal(ObjectInput in) throws IOException,
   ClassNotFoundException {
  System.out.println("In readExternal method");
  userName = (String)in.readObject();
  id = in.readInt();
 
 }
}

ExternalizableDemo class

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

public class ExternalizableDemo {

 public static void main(String[] args) {
  User user = new User("TestUser", 1, "pwd");
  try {
   ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.ser"));
   oos.writeObject(user);
   oos.close();
   
   ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test.ser"));

   user = (User)ois.readObject();
   ois.close();
   System.out.println("UserName " + user.getUserName() + " id " 
      + user.getId() + " pwd " + user.getPwd());
   
  } catch (IOException | ClassNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }
}

Output

In constructor with args
In writeExternal method
In no-arg constructor
In readExternal method
UserName TestUser id 1 pwd null

Here you can see that writeExternal() and readExternal() methods are called for serializing and deserializing the object. Now it is upto you to provide implementation for serialization in writeExternal() method where pwd field is excluded.

Points to note here are-

  1. A default no-arg constructor has to be there while using externalizable as object is created using no-arg constructor while deserializing and then the object is initialized using the logic in readExternal() method. Note that it is different from default serialization where object is reconstituted using the byte stream and constructor is not called.
  2. Order that is used in writeExternal() method for writing the fields should be maintained in readExternal() method.

Externalizable with inheritance

One scenario where Externalizable can be used quite effectively is in a parent-child relationship where parent class doesn’t implement the serializable interface but you want the fields that are there in the parent class to be serialized too.

In this case if you create an object of the child class and serialize it using default serialization. Then deserializing it won’t give you any value for the fields which child class object gets by virtue of extending parent class.

Let’s say we have a classA which is the super class and classB which extends classA.

ClassA

public class ClassA {
  private int deptId;
  private String deptName;
  public int getDeptId() {
   return deptId;
  }
  public void setDeptId(int deptId) {
   this.deptId = deptId;
  }
  public String getDeptName() {
   return deptName;
  }
  public void setDeptName(String deptName) {
   this.deptName = deptName;
  }
}

ClassB

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

public class ClassB extends ClassA implements Externalizable{
 private String empId;
 private String empName;
 public String getEmpId() {
   return empId;
 }
 public void setEmpId(String empId) {
   this.empId = empId;
 }
 public String getEmpName() {
   return empName;
 }
 public void setEmpName(String empName) {
   this.empName = empName;
 }
 @Override
 public void writeExternal(ObjectOutput out) throws IOException {
  System.out.println("In writeExternal method");
  //Writing parent class ClassA fields
  out.writeInt(getDeptId());
  out.writeObject(getDeptName());
  // Writing child class fields
  out.writeObject(getEmpId());
  out.writeObject(getEmpName());
  
 }

 @Override
 public void readExternal(ObjectInput in) throws IOException,
   ClassNotFoundException {
  System.out.println("In readExternal method");
  // Setting parent class fields
  setDeptId(in.readInt());
  setDeptName((String)in.readObject());
  // Setting child class fields
  setEmpId((String)in.readObject());
  setEmpName((String)in.readObject());
 }

}

Test class

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

public class ExternalizableDemo {

 public static void main(String[] args) {
    final String fileName = "D://test.ser";
    ClassB objb = new ClassB();
    objb.setDeptId(1);
    objb.setDeptName("Finance");
    objb.setEmpId("E001");
    objb.setEmpName("Ram");
  try {
   ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileName));
   oos.writeObject(objb);
   oos.close();
   
   ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName));

   objb = (ClassB)ois.readObject();
   ois.close();
   System.out.println("DeptId " + objb.getDeptId() + " DeptName " + objb.getDeptName() 
     + " EmpId " + objb.getEmpId() + " EmpName "+ objb.getEmpName());
   
  } catch (IOException | ClassNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }
}

Output

In writeExternal method
In readExternal method
DeptId 1 DeptName Finance EmpId E001 EmpName Ram

Since you can control what is serialized and how, you can make sure that all the fields of super class ClassA are also serialized and deserialized in the writeExternal() and readExternal() methods.

That's all for this topic Externalizable Interface in Java. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Java Advanced Tutorial Page


Related Topics

  1. SerialVersionUID And Versioning in Java Serialization
  2. Serialization Proxy Pattern in Java
  3. Marker Interface in Java
  4. Java Reflection API Tutorial
  5. Java Object Cloning - clone() Method

You may also like-

  1. How HashSet Works Internally in Java
  2. Java Collections Interview Questions And Answers
  3. Java Semaphore With Examples
  4. Java Concurrency Interview Questions And Answers
  5. Synchronization in Java - Synchronized Method And Block
  6. Bounded Type Parameter in Java Generics
  7. Java Variable Types With Examples
  8. Difference Between Checked And Unchecked Exceptions in Java

Wednesday, November 23, 2022

SerialVersionUID And Versioning in Java Serialization

If you ever implemented Serializable interface in Java then you would have seen the warning “The serializable class XXX does not declare a static final serialVersionUID field of type long”. If you ever wondered why this warning about serialVersionUID in your Java code then this post will help to understand it.


SerialVersionUID and versioning

A simple explanation for why do we need to declare serialVersionUID is; it helps with versioning.

Suppose you have some class which is serialized and it changes before it is deserialized. You will need to consider what happens in that situation? Can you allow the new version of your class to read old data.

To help with these versioning scenarios serialization process in Java provides a simple versioning mechanism using serialVersionUID.

Generating serialVersionUID

The stream-unique identifier is a 64-bit hash of the class name, interface class names, methods, and fields. If you are using IDE like eclipse and you have a class that implements Serializable interface then you will get a warning upfront that serialVersionUID is not declared.

Eclipse IDE also gives you options-

  • to add default serialVersionUID
  • OR
  • to add generated serialVersionUID

In case you choose to ignore that warning even then by default serialization mechanism in Java will generate serialVersionUID, both the name of the class and its serialVersionUID are written to the object stream.

During deserialization again serialVersionUID will be generated and compared with the previously written serialVersionUID, if there is a mismatch that means version is changed and InvalidClassException will be thrown.

SerialVersionUID Java example

Let’s try to clear it with an example. Suppose you have a Person class with few fields and you serialize the Person class. Before you try to deserialize the serialized Person object, you add a new field to the Person class.

So here is a Person class with fields like id, name and age. It implements Serializable interface and choose to ignore the warning to declare serialVersionUID.

public class Person implements Serializable{
 private String name;
 private int id;
 private int age;
 // Constructor
 Person(String name, int id, int age){
   System.out.println("In Constructor with args");
   this.name = name;
   this.id = id;
   this.age = age; 
 }
 // no-arg Constructor
 Person(){
   System.out.println("no-arg constructor");
 }
 
 public String getName() {
  return name;
 }
  
 public int getAge() {
  return age;
 }
 public int getId() {
  return id;
 }
}

Util class

This is a class with static methods to serialize and deserialize.

public class Util {
  /**
   * Method used for serialization
   * @param obj
   * @param fileName
   */
  public static void serialzeObject(Object obj, String fileName){
   try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File(fileName)))){
    oos.writeObject(obj);
    
   } catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }
  
  /**
   * Method used for deserializing
   * @param fileName
   * @return
   * @throws ClassNotFoundException
   */
  public static Object deSerialzeObject(String fileName) throws ClassNotFoundException{
   Object obj = null;
   try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File(fileName)))){
    obj = ois.readObject();
    
   } catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   return obj;
  }
}

Now I serialize Person class object using the following test class.

public class SerializationDemo {

 public static void main(String[] args) {
  // Creating and initializaing a Person object
    Person person = new Person("User1", 1, 22);
    // file name
    final String fileName = "F://person.ser";
    // serializing
    Util.serialzeObject(person, fileName);
    
    /*try {
     // deserializing
     person = (Person)Util.deSerialzeObject(fileName);
    } catch (ClassNotFoundException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }*/
 }
}

So far so good, Person class object is created and serialized. Now add a new field city to Person class.

public class Person implements Serializable{
 private String name;
 private int id;
 private int age;
 private String city;
 
 // Constructor
 Person(String name, int id, int age){
   System.out.println("In Constructor with args");
   this.name = name;
   this.id = id;
   this.age = age; 
 }
 // no-arg Constructor
 Person(){
   System.out.println("no-arg constructor");
 }
 
 public String getName() {
  return name;
 }
  
 
 public int getAge() {
  return age;
 }
 public int getId() {
  return id;
 }
 public String getCity() {
  return city;
 }
}

Now if I try to deserialize the byte stream which was already created before the inclusion of this new field in Person class InvalidClassException will be thrown.

public class SerializationDemo {
  public static void main(String[] args) {
    // Creating and initializaing a Person object
    Person person = new Person("User1", 1, 22);
    // file name
    final String fileName = "F://person.ser";
    // serializing
    //Util.serialzeObject(person, fileName);
    
    try {
      // deserializing
      person = (Person)Util.deSerialzeObject(fileName);
    } catch (ClassNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
}
java.io.InvalidClassException: org.netjs.prog.Person; local class incompatible: stream classdesc 
serialVersionUID = -4901887311122736183, local class serialVersionUID = -1818819755742473032
 at java.io.ObjectStreamClass.initNonProxy(Unknown Source)
 at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source)
 at java.io.ObjectInputStream.readClassDesc(Unknown Source)
 at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
 at java.io.ObjectInputStream.readObject0(Unknown Source)
 at java.io.ObjectInputStream.readObject(Unknown Source)
 at org.netjs.prog.Util.deSerialzeObject(Util.java:39)
 at org.netjs.prog.SerializationDemo.main(SerializationDemo.java:15)

Here note that even if you chose to ignore the warning and didn’t declare the serialVersionUID it is still generated. You can see that 2 different serialVersionUIDs are there as the class has changed and that mismatch has caused the InvalidClassException.

Assigning serialVersionUID to Java class

As shown in the above example if you choose to ignore the warning and rely on generation of serialVersionUID by the serialization mechanism itself it will always fail if there is a change in the class.

That’s why you as an implementor of the class should take charge and assign a serialVersionUID yourself (If you are using IDE like Eclipse that can be generated by Eclipse for you or you can use serialver tool which comes with the JDK to generate serialVersionUID).

With you taking charge when you know your class has changed in a way that it is not compatible with the old version anymore you can choose to change the serialVersionUID. In that case during deserialization because of the non-matching serialVersionUID, InvalidClassException will be thrown.

If you choose not to change the serialVersionUID even if your class has changed as you think the change is not significant then deserialization will proceed with out throwing any exception.

Let’s take the same example as above but this time serialVersionUID is declared.

Person class

public class Person implements Serializable{
 private static final long serialVersionUID = -4046333379855427853L;
 private String name;
 private int id;
 private int age;
 /*private String city;
 public String getCity() {
  return city;
 }*/
 // Constructor
 Person(String name, int id, int age){
   System.out.println("In Constructor with args");
   this.name = name;
   this.id = id;
   this.age = age; 
 }
 // no-arg Constructor
 Person(){
   System.out.println("no-arg constructor");
 }
 
 public String getName() {
  return name;
 }
   
 public int getAge() {
  return age;
 }
 public int getId() {
  return id;
 }
}

Now the class is serialized.

public class SerializationDemo {

 public static void main(String[] args) {
  // Creating and initializaing a Person object
    Person person = new Person("User1", 1, 22);
    // file name
    final String fileName = "F://person.ser";
    // serializing
    //Util.serialzeObject(person, fileName);
    
    /*try {
     // deserializing
     person = (Person)Util.deSerialzeObject(fileName);
     System.out.println("id " + person.getId() + " Name "+ person.getName() 
       + " Age " + person.getAge() + " City " + person.getCity());
    } catch (ClassNotFoundException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }*/
 }
}

Now a new field city is added in the Person class but the serialVersionUID remains same as before.

public class Person implements Serializable{

 private static final long serialVersionUID = -4046333379855427853L;
 private String name;
 private int id;
 private int age;
 private String city;
 public String getCity() {
  return city;
 }
 // Constructor
 Person(String name, int id, int age){
   System.out.println("In Constructor with args");
   this.name = name;
   this.id = id;
   this.age = age; 
 }
 // no-arg Constructor
 Person(){
   System.out.println("no-arg constructor");
 }
 
 public String getName() {
  return name;
 }
   
 public int getAge() {
  return age;
 }
 public int getId() {
  return id;
 }
}

Now deserialization will happen though there won’t be any value for the city field.

public class SerializationDemo {

 public static void main(String[] args) {
  // Creating and initializaing a Person object
    Person person = new Person("User1", 1, 22);
    // file name
    final String fileName = "F://person.ser";
    // serializing
    //Util.serialzeObject(person, fileName);
    
    try {
     // deserializing
     person = (Person)Util.deSerialzeObject(fileName);
     System.out.println("id " + person.getId() + " Name "+ person.getName() 
       + " Age " + person.getAge() + " City " + person.getCity());
    } catch (ClassNotFoundException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
 }
}

Output

In Constructor with args
id 1 Name User1 Age 22 City null

For test you can regenerate the serialVersionUID of the Person class after adding city field. In that case deserialization will fail as there will be a mismatch between serialVersionUIDs.

Points to remember

  1. serialVersionUID is used for versioning of the serialized streams. During serialization process serialVersionUID is also stored. During deserialization generated serialVersionUID is matched with the stored one and if there is a mismatch process fails.
  2. serialVersionUID is a 64-bit hash of the class name, interface class names, methods, and fields. If you don’t declare one yourself serialization process will still generate serialVersionUID. In that case it will fail for any change in the class.
  3. If you declare the serialVersionUID that gives you control over the versioning. When you think class has grown in way that is not compatible with the previous versions then you can change the serialVersionUID. If you think change in the class are not significant enough to change the serialVersionUID you may choose to retain the same serialVersionUID. In that case serialization and deserialization will not fail even if your class had changed.
  4. serialVersionUID is declared as a private static final long and it is always better to declare one in order to have control over the versioning of the class.

That's all for this topic SerialVersionUID And Versioning in Java Serialization. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Java Advanced Tutorial Page


Related Topics

  1. Serialization and Deserialization in Java
  2. Transient Keyword in Java With Examples
  3. Externalizable Interface in Java
  4. Serialization Proxy Pattern in Java
  5. Marker interface in Java

You may also like-

  1. How HashSet Works Internally in Java
  2. Fail-Fast Vs Fail-Safe Iterator in Java
  3. Java Phaser With Examples
  4. Java ReentrantReadWriteLock With Examples
  5. Java Stream API Tutorial
  6. Lambda Expressions in Java 8
  7. Type Wrapper Classes in Java
  8. Try-With-Resources in Java With Examples

Tuesday, November 22, 2022

Serialization Proxy Pattern in Java

When you serialize an object in Java it is converted to byte stream and object is reconstituted using that byte stream during the process of deserialization. Sometimes this extraneous behavior of creating object using the byte stream is not what you want and you still want constructor (or any other method if required) to be called when your object is created during the process of deserialization.

In a scenario like this you can use serialization proxy pattern where you serialize a proxy object rather than the real object, at the time of deserialization using that proxy object you can create the real object by calling the constructor (or any other required method).

Serialization proxy pattern

Serialization proxy pattern is a way to design your class where proxy pattern defines its serialization mechanism.

Before going into any more details about serialization proxy pattern in Java let’s know about two methods.

  • writeReplace()
  • readResolve()

writeReplace() method in Java

Serializable classes that use an alternative object (proxy object) when writing an object to the stream should implement writeReplace() method with the exact signature:

ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;

This writeReplace() method is invoked by serialization if the method exists and this method is defined within the original class whose object is serialized. Thus, the method can have private, protected and package-private access modifier.

readResolve() method in Java

Classes that need to provide a replacement object when the serialized object is read from the stream should implement readResolve() method with the exact signature.

 ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;

This readResolve() method follows the same invocation rules and accessibility rules as writeReplace.

How Serialization Proxy pattern works in Java

Rather than serializing the original class you provide functionality using the writeReplace() method to serialize the proxy class instead. Here note that writeReplace() method is implemented in the original class.

At the time of deserialization proxy object is deserialized and then the readResolve() method is called. That’s where you will have to provide the functionality to create the original class object regular way. Here note that readResolve() method is implemented in the proxy class.

Proxy class

Generally serialization proxy pattern is implemented by creating a proxy class as a nested static class with in the original class. Since it needs access to the fields of the outer class so that it is better to create proxy class as a nested class.

Serialization proxy pattern example

Time is right to see an example of the serialization proxy pattern in Java. Here we have a Person class which has a constructor with args. When a Person class object is created it is initialized using this constructor and that’s what you want to do when you deseliarize a serialized Person class object.

For doing that you will use Serialization proxy pattern and create a proxy class (called PersonProxy here) as a nested static class. You will also implement writeReplace() and readResolve() methods.

Person class

import java.io.Serializable;

public class Person implements Serializable{
 
 private static final long serialVersionUID = 9140203997753929147L;
 private String name;
 private int id;
 private int age;
 
 // Constructor
 Person(String name, int id, int age){
   System.out.println("In Constructor with args");
   this.name = name;
   this.id = id;
   this.age = age; 
 }
 // no-arg Constructor
 Person(){
   System.out.println("no-arg constructor");
 }
 
 public String getName() {
  return name;
 }
  
 public int getAge() {
  return age;
 }
 public int getId() {
  return id;
 }
 /**
  * writeReplace method for the proxy pattern
  * @return
  */
 private Object writeReplace() {
  System.out.println("In writeReplace() method");
  return new PersonProxy(this);
 }
 // Nested static class - Proxy
 private static class PersonProxy implements Serializable {

  private static final long serialVersionUID = -5965328891170223339L;
  private String name;
  private int id;
  private int age;
  PersonProxy(Person p) {
   this.name = p.name;
   this.id = p.id;
   this.age = p.age;
  }
  // readResolve method for Person.PersonProxy
  private Object readResolve() {
   System.out.println("In readResolve() method");
   return new Person(name, id, age); // Uses public constructor
  } 
 }
}

Util class

A util class with methods to serialize and deserialize.

import java.io.File;
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 Util {
  /**
   * Method used for serialization
   * @param obj
   * @param fileName
   */
  public static void serialzeObject(Object obj, String fileName){
   try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File(fileName)))){
    oos.writeObject(obj);
    
   } catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }
  
  /**
   * Method used for deserializing
   * @param fileName
   * @return
   * @throws ClassNotFoundException
   */
  public static Object deSerialzeObject(String fileName) throws ClassNotFoundException{
   Object obj = null;
   try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File(fileName)))){
    obj = ois.readObject();
    
   } catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   return obj;
  }
}

SerializationDemo class

Using this class a Person class object will be serialized and later deserialized.

public class SerializationDemo {

 public static void main(String[] args) {
  // Creating and initializaing a Person object
    Person person = new Person("User1", 1, 22);
    // file name
    final String fileName = "F://person.ser";
    System.out.println("About to serialize ....");
    // serializing
    Util.serialzeObject(person, fileName);
    
    try {
     System.out.println("About to deserialize ....");
     // deserializing
     person = (Person)Util.deSerialzeObject(fileName);
     System.out.println("id " + person.getId() + " Name "+ person.getName() 
       + " Age " + person.getAge());
    } catch (ClassNotFoundException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
 }
}

Output

In Constructor with args
About to serialize ....
In writeReplace() method
About to deserialize ....
In readResolve() method
In Constructor with args
id 1 Name User1 Age 22

Here you can see that writeReplace() method is called when object is serialized. At the time of deserializing the object readResolve() method is called where object is created and initialized using the constructor of the class not just recreated using the byte stream.

Creating instance by getting data from DB

Another serialization proxy pattern usage example, which you will see in many frameworks too is when you want your instance to be created using a DB call. In that case what you need to serialize is some identifier only and during deserialization using that identifier you will get the data to construct the object from DB.

Here you have a Person class with fields as id, name etc. In writeReplace() method where you serialize the proxy you provide the id also.

Person class

public class Person implements Serializable {
 private int id;
 private String name;
 …
 private Object writeReplace() {
  return new PersonProxy(id);
 }
}

Proxy Class

In the readResolve() method of the proxy class you create the Person class instance using the id which you saved earlier.

public class PersonProxy implements Serializable {
 private int id;
 public PersonProxy(int id) {
  this.id = id;
 }
 public Object readResolve() {
  // DB call to get the person record by id
  return PersonDAO.findById(id);
 }
}

That's all for this topic Serialization Proxy pattern in Java. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Java Advanced Tutorial Page


Related Topics

  1. Transient in Java
  2. Externalizable Interface in Java
  3. SerialVersionUID And Versioning in Java Serialization
  4. Serialization and Deserialization in Java
  5. Spring NamedParameterJdbcTemplate Insert, Update And Delete Example

You may also like-

  1. @FunctionalInterface Annotation in Java
  2. Spliterator in Java
  3. Type Erasure in Java Generics
  4. Java ThreadLocal Class With Examples
  5. Java BlockingDeque With Examples
  6. Multi-Catch Statement in Java Exception Handling
  7. BigDecimal in Java With Examples
  8. Java Program to Find The Longest Palindrome in a Given String

Thursday, August 18, 2022

Serialization and Deserialization in Java

Object serialization is the mechanism of converting object into byte stream. Where as the reverse process of recreating the object from those serialized bytes is known as deserialization. In this post we'll see the process of serialization and deserialization in Java.


Requirement for serialization in Java

A mandatory requirement for a class object to be able to be serialized in Java is that the class should implement Serializable interface which is part of java.io package. Here note that Serializable is a marker interface and does not have any field or method.

Steps for object Serialization in Java

In order to serialize an object in Java you need to do the following-

  • Create an object of ObjectOutputStream.
  • Create an object of OutputStream to be wrapped inside ObjectOutputStream object.
  • Then you need to call writeObject() method which will serialize your object and send it to OutputStream.

Steps for object Deserialization in Java

In order to deserialize an object in Java you need to do the following-

  • Create an object of ObjectInputStream.
  • Create an object of InputStream to be wrapped inside ObjectInputStream object.
  • Then you need to call readObject() method which will reconstitute your object from the byte stream.

Use of serialization in Java

Once an object is converted to a byte stream those bytes can be-

  1. Transmitted from one JVM to another where the object can be reconstituted using those bytes and used in further processing.
  2. Stored on a disk/DB for further use.
Serialization in Java

Java Serialization and Deserialization example

Let’s see an example of serialization and deserialization in Java, here we have an Address bean with fields like addressline1, city etc. and a Person bean that along with other fields also has an instance of address class. That way you can see that the whole object graph is serialized.

Then we have Util class with the methods to serialize and deserialize an object. Also the test class SerializationDemo to execute the code.

Address class

public class Address implements Serializable{
 private String addressLine1;
 private String addressLine2;
 private String city;
 private String state;
 private String country;
 
 Address(String addressLine1, String addressLine2, String city, String state, String country){
  this.addressLine1 = addressLine1;
  this.addressLine2 = addressLine2;
  this.city = city;
  this.state = state;
  this.country = country;
 }
 public String getAddressLine1() {
  return addressLine1;
 }

 public String getAddressLine2() {
  return addressLine2;
 }
 
 public String getCity() {
  return city;
 }
 
 public String getState() {
  return state;
 }
 
 public String getCountry() {
  return country;
 }
}

Person class

public class Person implements Serializable{

 private static final long serialVersionUID = 1L;
 private String name;
 private Date dob;
 // transient
 private transient int age;
 private Address address;
 // Constructor
 Person(String name, Date dob, int age, Address address){
  System.out.println("In Constructor with args");
  this.name = name;
  this.dob = dob;
  this.age = age;
  this.address = address;    
 }
 // no-arg Constructor
 Person(){
  System.out.println("no-arg constructor");
 }
 
 public Address getAddress() {
  return address;
 }

 public String getName() {
  return name;
 }
 
 public Date getDob() {
  return dob;
 }
 
 public int getAge() {
  return age;
 }
}

Util class

import java.io.File;
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 Util {
 
 /**
  * Method used for serialization
  */
 public static void serialzeObject(Object obj, String fileName){
  try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
      new File(fileName)))){
   oos.writeObject(obj);
   
  } catch (FileNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }
 
 /**
  * Method used for deserializing
  */
 public static Object deSerialzeObject(String fileName) throws ClassNotFoundException{
  Object obj = null;
  try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File(fileName)))){
   obj = ois.readObject();
   
  } catch (FileNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  return obj;
 }

}

SerializationDemo class

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;


public class SerializationDemo {
 public static void main(String[] args) {
  DateFormat df = new SimpleDateFormat("dd/MM/yyyy");
  Date dob = null;
  try {
   dob = df.parse("04/04/2015");
  } catch (ParseException e1) {
   // TODO Auto-generated catch block
   e1.printStackTrace();
  }
  // Creating and initializaing a Address object
  Address address = new Address("#34", "ABC Street", "New Delhi", "Delhi", "India");
  // Creating and initializaing a Person object
  Person person = new Person("User1", dob, 2, address);
  // file name
  final String fileName = "D://person.ser";
  // serializing
  Util.serialzeObject(person, fileName);
  
  try {
   // deserializing
   person = (Person)Util.deSerialzeObject(fileName);
  } catch (ClassNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  System.out.println("deserialized object ---- ");
  System.out.println("Name " + person.getName());
  System.out.println("DOB " + person.getDob());
  System.out.println("Age " + person.getAge());
  System.out.println("Address Line1 " + person.getAddress().getAddressLine1());
  System.out.println("Address Line2 " + person.getAddress().getAddressLine2());
  System.out.println("City " + person.getAddress().getCity());
  System.out.println("State " + person.getAddress().getState());
  System.out.println("Country " + person.getAddress().getCountry());
  
 }
}

Output

In Constructor with args
deserialized object ---- 
Name User1
DOB Sat Apr 04 00:00:00 IST 2015
Age 0
Age #34
Age ABC Street
Age New Delhi
Age Delhi
Age India

In SerializationDemo class you specify the file name and then pass the person object and the file name to the serializeObject method of the Util class which will do the process of serialization. Again you pass the filename which already has the stored bytes for the person object to the deSerializeObject method of the Util class which will deserialize the serialized person object.

Points to note here

  • Here you can see in person object there is a reference to address object and both are serialized which shows the whole object graph gets serialized even if there is a very complex hierarchy.
  • All the classes which are to be serialized should be implementing Serializable interface. In this example suppose Address class doesn’t implement Serializable interface and there is a reference of it in Person class then you will get exception when you try to serialize the person object.
    java.io.NotSerializableException: org.test.Address
  • If you have noticed in Person class there is a system.println in both with args and no-arg constructors. When the object is deserialized you can see even default no-arg constructor is not called. Though reiterated several times I’ll repeat it again that object is reconstituted from the serialized bytes while deserializing and no constructor is called during the process of deserialization.
  • In Person class age field is marked as transient which stops this field from getting serialized. That’s why you can see age is printed as 0. Refer Transient in Java to know more about transient keyword.

Implementing writeObject() and readObject() methods

Default mechanism for serialization in Java for us, as end user, is automatic. If you want to have some control over the process of serialization and deserialization in Java then you can implement writeObject() and readObject() methods in your own class.

If you add methods writeObject() and readObject(), these methods will be called automatically when the object is serialized and deserialized respectively and provide the serialization mechanism rather than using the default mechanism. General form of writeObject() and readObject() methods is as follows.

private void writeObject(ObjectOutputStream oos) throws IOException

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException

writeObject() and readObject() methods - Serialization with inheritance

One scenario where writeObject() and readObject() methods can be used quite effectively is in a parent-child relationship where parent class doesn’t implement the serializable interface but you want the fields that are there in the parent class to be serialized too. Let’s say we have a classA which is the super class and classB which extends classA.

ClassA

public class ClassA {
 private int deptId;
 private String deptName;
 public int getDeptId() {
  return deptId;
 }
 public void setDeptId(int deptId) {
  this.deptId = deptId;
 }
 public String getDeptName() {
  return deptName;
 }
 public void setDeptName(String deptName) {
  this.deptName = deptName;
 }
 
}

ClassB

import java.io.Serializable;

public class ClassB extends ClassA implements Serializable{
 /**
  * 
  */
 private static final long serialVersionUID = 7280011452607087533L;
 private String empId;
 private String empName;
 public String getEmpId() {
  return empId;
 }
 public void setEmpId(String empId) {
  this.empId = empId;
 }
 public String getEmpName() {
  return empName;
 }
 public void setEmpName(String empName) {
  this.empName = empName;
 }
 
}

In this case if you create a ClassB object and serialize it. Then deserializing it won’t give you any value for the fields which ClassB object gets by virtue of extending ClassA.

public class TestSerialize{
 
 public static void main(String[] args) {
  final String fileName = "D://test.ser";
  ClassB objb = new ClassB();
  objb.setDeptId(1);
  objb.setDeptName("Finance");
  objb.setEmpId("E001");
  objb.setEmpName("Ram");
  Util.serialzeObject(objb, fileName);
  try {
   objb = (ClassB)Util.deSerialzeObject(fileName);
  } catch (ClassNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  System.out.println("DeptId " + objb.getDeptId() + " DeptName " + objb.getDeptName());
  System.out.println("EmpId " + objb.getEmpId() + " EmpName " + objb.getEmpName());
 }

}

Output

DeptId 0 DeptName null
EmpId E001 EmpName Ram

You can see that the deptId and deptName fields which are in ClassA are not serialized as ClassA doesn’t implement Serializable interface.

In this case you will have to add writeObject and readObject methods and provide functionality so that the fields of ClassA are also serialized. So, your updated ClassB with writeObject() and readObject() methods added will look like –

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class ClassB extends ClassA implements Serializable{
 /**
  * 
  */
 private static final long serialVersionUID = 7280011452607087533L;
 private String empId;
 private String empName;
 public String getEmpId() {
  return empId;
 }
 public void setEmpId(String empId) {
  this.empId = empId;
 }
 public String getEmpName() {
  return empName;
 }
 public void setEmpName(String empName) {
  this.empName = empName;
 }
 
 // adding writeObject method
 private void writeObject(ObjectOutputStream oos) throws IOException{
  // calling default functionality for classB fields
  oos.defaultWriteObject();
  // Explicitly writing ClassA fields
  oos.writeInt(getDeptId());
  oos.writeObject(getDeptName());
 }
 
 // adding readObject method
 private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException{
  // calling default functionality for classB fields
  ois.defaultReadObject();
  // Explicitly reading ClassA fields and setting them
  setDeptId(ois.readInt());
  setDeptName((String)ois.readObject());
 }
}

Output

DeptId 1 DeptName Finance
EmpId E001 EmpName Ram

Running it using the TestSerialize class will now give you proper output.

Portability with Java serialization

One important feature of serialization in Java is the portability it offers. When a serialized object is transmitted across the network the serialization mechanism will take into account the differences in operating systems. Thus an object converted into byte streams in Windows OS can be transmitted across the network and deserialized into an object in the Unix operating system.

Points to note here

  1. One thing to be careful about here is that the order of reading the fields should be same as the order they were written.
  2. You can use defaultWriteObject() and defaultReadObject() as the first statement in writeObject() and readObject() methods respectively for using the default serialization mechanism.

That's all for this topic Serialization and Deserialization in Java. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Java Advanced Tutorial Page


Related Topics

  1. Externalizable Interface in Java
  2. Transient Keyword in Java With Examples
  3. SerialVersionUID And Versioning in Java Serialization
  4. Marker Interface in Java
  5. Java Object Cloning - clone() Method

You may also like-

  1. How ArrayList Works Internally in Java
  2. Difference Between ArrayList And CopyOnWriteArrayList in Java
  3. Fail-Fast Vs Fail-Safe Iterator in Java
  4. Java ReentrantReadWriteLock With Examples
  5. Spliterator in Java
  6. Exception Handling in Java Lambda Expressions
  7. BigDecimal in Java With Examples
  8. Is String Thread Safe in Java

Thursday, August 11, 2022

Transient Keyword in Java With Examples

In the post Serialization in Java it has already been discussed that serialization is the mechanism by which any object can be converted to byte stream. By default all of the object variables are converted to bytes and persisted/send across network.There may be a scenario where you don’t want a variable to be part of serialization because that variable may have some sensitive information which you don’t want to persist or send across the network. That variable can be marked as transient in Java.

Transient keyword in Java

If you want to exclude any object field from getting serialized you can mark that field as transient in Java. Note that you cannot use transient keyword with methods or local variables it can only be used with member variables.

Java transient keyword example

Let’s see an example using transient keyword. Here we have a class called User which has a field pwd which you don’t want to get serialized.

There is a also a class Util that has static methods for serializing and deserializing an object.

User class

import java.io.Serializable;
import java.util.Date;
public class User implements Serializable{
 /**
  * 
  */
 private static final long serialVersionUID = 1L;
 private String userName;
 private Date loginDate;
 // Transient field
 private transient String pwd;
 public String getUserName() {
  return userName;
 }
 public void setUserName(String userName) {
  this.userName = userName;
 }
 public Date getLoginDate() {
  return loginDate;
 }
 public void setLoginDate(Date loginDate) {
  this.loginDate = loginDate;
 }
 public String getPwd() {
  return pwd;
 }
 public void setPwd(String pwd) {
  this.pwd = pwd;
 }

}

Util class

import java.io.File;
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 Util {
 
 /**
  * Method used for serialization
  * @param obj
  * @param fileName
  */
 public static void serialzeObject(Object obj, String fileName){
  try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
     new File(fileName)))){
   oos.writeObject(obj);
   
  } catch (FileNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }
 
 /**
  * Method used for deserializing
  * @param fileName
  * @return
  * @throws ClassNotFoundException
  */
 public static Object deSerialzeObject(String fileName) throws ClassNotFoundException{
  Object obj = null;
  try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
     new File(fileName)))){
   obj = ois.readObject();
   
  } catch (FileNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  return obj;
 }

}

Now let’s create a User object and serialize and deserialize it to see what happens to the pwd field.

import java.util.Date;

public class SerializationDemo {
 public static void main(String[] args) {
  // Creating and initializaing a User object
  User user = getUser();
  // file name
  final String fileName = "D://user.ser";
  // serializing
  Util.serialzeObject(user, fileName);
  try {
   // deserializing
   user = (User)Util.deSerialzeObject(fileName);
  } catch (ClassNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  System.out.println("User name " + user.getUserName());
  System.out.println("Login date " + user.getLoginDate());
  System.out.println("Password " + user.getPwd());
  
 }
 
 private static User getUser(){
  User user = new User();
  user.setUserName("TestUser");
  user.setLoginDate(new Date());
  user.setPwd("pwd@123");
  return user;
 }
}

Output

User name TestUser
Login date Mon Apr 03 18:08:49 IST 2017
Password null

If you open the created file “user.ser” you won’t find any trace of pwd variable there.

When to use transient keyword in Java

  1. As mentioned above you can use transient keyword with the variable that holds some confidential value which you don’t want to get persisted in a file or sent across the network.
  2. Any variable that is calculated using other variables can be marked as transient in Java. After deserialization you can get the other variables and recalculate the value. One of the example of this can be found in Java Collection framework itself. In code of HashMap class you can see that hash code is calculated again after deserializing the HashMap key and values. Hashcode is not serialized but recalculated in that case.

    Let’s say you have a Person bean where you have both Date Of Birth and age fields. In that case age can be calculated again using the DOB and current date rather than serializing the age. If you are serializing the Person object for long age may be wrong if taken from the deserialized object.

    Person class

    import java.io.Serializable;
    import java.util.Date;
    
    public class Person  implements Serializable{
    
     private static final long serialVersionUID = 1L;
     private String name;
     private Date dob;
     // transient
     private transient int age;
     public String getName() {
      return name;
     }
     public void setName(String name) {
      this.name = name;
     }
     public Date getDob() {
      return dob;
     }
     public void setDob(Date dob) {
      this.dob = dob;
     }
     public int getAge() {
      return age;
     }
     public void setAge(int age) {
      this.age = age;
     }
    }
    

    SerializationDemo class

    import java.text.DateFormat;
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.time.LocalDate;
    import java.time.temporal.ChronoUnit;
    import java.util.Calendar;
    import java.util.GregorianCalendar;
    
    public class SerializationDemo {
     public static void main(String[] args) {
      // Creating and initializaing a Person object
      Person person = getPerson();
      // file name
      final String fileName = "D://person.ser";
      // serializing
      Util.serialzeObject(person, fileName);
      
      try {
       // deserializing
       person = (Person)Util.deSerialzeObject(fileName);
      } catch (ClassNotFoundException e) {
       // TODO Auto-generated catch block
       e.printStackTrace();
      }
      
      System.out.println("Name " + person.getName());
      System.out.println("DOB " + person.getDob());
      System.out.println("Age " + person.getAge());
      
      int age = (int)calculateAge(person);
      System.out.println("age " +age);
     }
     
     /**
      * Method for getting person record
      * @return
      */
     private static Person getPerson(){
      Person person = new Person();
      DateFormat df = new SimpleDateFormat("dd/MM/yyyy");
      try {
       person.setDob(df.parse("04/04/2015"));
      } catch (ParseException e) {
       // TODO Auto-generated catch block
       e.printStackTrace();
      }
      person.setName("TestUser");
      return person;
     }
     
     /**
      * Method for calculating age
      * @param person
      * @return
      */
     private static long calculateAge(Person person){
      // creating a calendar instance with DOB
      Calendar firstCal = GregorianCalendar.getInstance();
      firstCal.setTime(person.getDob());
      // Calculating difference in years
      long num = ChronoUnit.YEARS.between( LocalDate.of(firstCal.get(Calendar.YEAR), firstCal.get(Calendar.MONTH), 
         firstCal.get(Calendar.DAY_OF_MONTH)) , LocalDate.now()) ;
      
      return num;
     }
    
    }
    

    Util class is already given above with the serialize() and deserialize() methods.

    Output

    Name TestUser
    DOB Sat Apr 04 00:00:00 IST 2015
    Age 0
    age 2
    
    You can see in the output initially age is 0 after deserialization as age was marked as transient. It’s later calculated using the DOB and current date.
  3. You may have instances that have no meaning if serialized. Like a logger instance, a resource bundle, a properties instance with property values, it's better to mark such instances as transient.

That's all for this topic Transient Keyword in Java With Examples. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Java Advanced Tutorial Page


Related Topics

  1. Externalizable Interface in Java
  2. SerialVersionUID And Versioning in Java Serialization
  3. Serialization Proxy Pattern in Java
  4. Java Object Cloning - clone() Method
  5. Core Java Basics Interview Questions And Answers

You may also like-

  1. How LinkedList Class Works Internally in Java
  2. Fail-Fast Vs Fail-Safe Iterator in Java
  3. Difference Between Comparable and Comparator in Java
  4. Difference Between CountDownLatch And CyclicBarrier in Java
  5. AtomicInteger in Java With Examples
  6. Race Condition in Java Multi-Threading
  7. Volatile Keyword in Java With Examples
  8. Type Casting in Java With Conversion Examples