Serialization in Java

Serialization enables us to represent an object as a sequence of bytes that includes the object’s data as well as information about the object’s type and the types of data stored in the object.

After a serialized object has been written into a file, it can be read from the file and deserialized that is, the type information and bytes that represent the object and its data can be used to recreate the object in memory.

Entire process is JVM independent, meaning an object can be serialized on one platform and deserialized on an entirely different platform.

Classes and methods for serialization:

ObjectOutputStream class contains many write methods for serializing an object.
public final void writeObject(Object x) throws IOException

Above method serializes an Object and sends it to the output stream.

ObjectInputStream class contains the following method for deserializing an object:
public final Object readObject() throws IOException, ClassNotFoundException
The return value is Object, so you will need to cast it to its appropriate data type.

 

For a class to be serialized successfully, two conditions must be met:

  • The class must implement the java.io.Serializable interface.
  • All of the fields in the class must be serializable. If a field is not serializable, it must be marked transient.

 

When serializing an object to a file, the standard convention in Java is to give the file a .ser extension.

Serialization of class
public class Employee implements java.io.Serializable
{
   public String name;
   public String address;
   public transient int SSN;
   public int number;
   
   public void mailCheck()
   {
      System.out.println("Mailing a check to " + name + " " + address);
   }
}

Serializing an object
import java.io.*;
public class SerializeDemo
{
   public static void main(String [] args)
   {
      Employee e = new Employee();
      e.name = "Reyan Ali";
      e.address = "Phokka Kuan, Ambehta Peer";
      e.SSN = 11122333;
      e.number = 101;
      
      try
      {
         FileOutputStream fileOut = new FileOutputStream("/tmp/employee.ser");
         ObjectOutputStream out = new ObjectOutputStream(fileOut);
         out.writeObject(e);
         out.close();
         fileOut.close();
         System.out.printf("Serialized data is saved in /tmp/employee.ser");
      }catch(IOException i)
      {
          i.printStackTrace();
      }
   }
}

Deserializing an object:
import java.io.*;
public class DeserializeDemo
{
   public static void main(String [] args)
   {
      Employee e = null;
      try
      {
         FileInputStream fileIn = new FileInputStream("/tmp/employee.ser");
         ObjectInputStream in = new ObjectInputStream(fileIn);
         e = (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("Name: " + e.name);
      System.out.println("Address: " + e.address);
      System.out.println("SSN: " + e.SSN);
      System.out.println("Number: " + e.number);
    }
}


The try/catch block tries to catch a ClassNotFoundException, which is declared by the readObject() method.

For a JVM to be able to deserialize an object, it must be able to find the bytecode for the class.

If the JVM can’t find a class during the deserialization of an object, it throws a ClassNotFoundException.

 

Class Refactoring with Serialization and serialVersionUID

Serialization in java permits some changes in the java class if they can be ignored. Some of the changes in class that will not affect the deserialization process are:

  • Adding new variables to the class
  • Changing the variables from transient to non-transient, for serialization it’s like having a new field.
  • Changing the variable from static to non-static, for serialization it’s like having a new field.

But for all these changes to work, the java class should have serialVersionUID defined for the class.

If the class doesn’t define serialVersionUID, it’s getting calculated automatically and assigned to the class. Java uses class variables, methods, class name, package etc to generate this unique long number. If you are working with any IDE, you will automatically get a warning that “The serializable class Employee does not declare a static final serialVersionUID field of type long”.

We can use java utility “serialver” to generate the class serialVersionUID, for Employee class we can run it with below command.

SerializationExample/bin$serialver -classpath . com.journaldev.serialization.Employee

serialVersionUID tells the deserialization process that the new class is the new version of the same class and should be deserialized of possible.

Declaration of serialVersionUID in a class:
private static final long serialVersionUID = 2087368867376448459L;

 

Java Externalizable Interface:

Sometimes we want to obscure the object data to maintain it’s integrity. We can do this by implementing java.io.Externalizable interface and provide implementation of writeExternal() and readExternal() methods to be used in serialization process.

@Override
public void writeExternal(ObjectOutput out) throws IOException {
	out.writeInt(id);
	out.writeObject(name+"xyz");
	out.writeObject("abc"+gender);
}

@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
	id=in.readInt();
	//read in the same order as written
	name=(String) in.readObject();
	if(!name.endsWith("xyz")) throw new IOException("corrupted data");
	name=name.substring(0, name.length()-3);
	gender=(String) in.readObject();
	if(!gender.startsWith("abc")) throw new IOException("corrupted data");
	gender=gender.substring(3);
}

If these methods are present in the class, they are used for serialization purposes.

readObject(ObjectInputStream ois): If this method is present in the class, ObjectInputStream readObject() method will use this method for reading the object from stream.

writeObject(ObjectOutputStream oos): If this method is present in the class, ObjectOutputStream writeObject() method will use this method for writing the object to stream. One of the common usage is to obscure the object variables to maintain data integrity.

Object writeReplace(): If this method is present, then after serialization process this method is called and the object returned is serialized to the stream.

Object readResolve(): If this method is present, then after deserialization process, this method is called to return the final object to the caller program. One of the usage of this method is to implement Singleton pattern with Serialized classes.

Links for further details: http://www.journaldev.com/2452/serialization-in-java, http://www.tutorialspoint.com/java/java_serialization.htm

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s