Serialization in Java

Serialization is the process of converting Java objects into a stream of bytes. The stream of bytes can be transmitted through a network connection, stored in a database as a BLOB object or saved as a binary file. The stored or transmitted stream of bytes can be reconstructed to Java object later. This article explains Serialization in Java, using a simple step by step example.
Note: The sample codes provided in this tutorial, use try-with-resources blocks to auto close the input and output streams. You need Java 7 or latest version to compile and run these codes. For more information about try-with-resources, visit to this tutorial: Try With Resources
Create a Student class with the attributes name, and age.
public class Student {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getAge() {
        return age;
    }
}
Create a Main class as shown below, to create an object of Student.
public class Main {

    public static void main(String[] args) {
        Student stu = createStudent();
        System.out.println("Name: " + stu.getName());
        System.out.println("Age: " + stu.getAge());
    }

    /**
     * Create a sample Student object.
     *
     * @return a Student object.
     */
    public static Student createStudent() {
        // Create a Student object
        Student stu = new Student();
        stu.setName("Alice");
        stu.setAge(24);

        return stu;
    }
}

As defined earlier, serialization is the process of converting an object to a stream of bytes. Don't be afraid. The term stream of bytes refers just an array of bytes. To serialize the Student object, add a new method toStream to the Main class.
/**
 * Convert a Student object into stream of bytes.
 *
 * @param stu
 *            Student object.
 * @return stream of bytes
 */
public static byte[] toStream(Student stu) {
    // Reference for stream of bytes
    byte[] stream = null;
    // ObjectOutputStream is used to convert a Java object into OutputStream
    try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);) {
        oos.writeObject(stu);
        stream = baos.toByteArray();
    } catch (IOException e) {
        // Error in serialization
        e.printStackTrace();
    }
    return stream;
}
The Main class with toStream method is provided here:
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Arrays;

public class Main {

    public static void main(String[] args) {
        Student stu = createStudent();
        System.out.println("Name: " + stu.getName());
        System.out.println("Age: " + stu.getAge());

        // Convert the object to stream
        byte[] stream = toStream(stu);

        // Print the array
        System.out.println(Arrays.toString(stream));
    }

    /**
     * Create a sample Student object.
     *
     * @return a Student object.
     */
    public static Student createStudent() {
        // Create a Student object
        Student stu = new Student();
        stu.setName("Alice");
        stu.setAge(24);

        return stu;
    }

    /**
     * Convert a Student object into stream of bytes.
     *
     * @param stu
     *            Student object.
     * @return stream of bytes
     */
    public static byte[] toStream(Student stu) {
        // Reference for stream of bytes
        byte[] stream = null;
        // ObjectOutputStream is used to convert a Java object into OutputStream
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(baos);) {
            oos.writeObject(stu);
            stream = baos.toByteArray();
        } catch (IOException e) {
            // Error in serialization
            e.printStackTrace();
        }
        return stream;
    }
}
If you run this modified code, you will end up with java.io.NotSerializableException. The reason is: to convert any objects of a class into a stream, a special permission has to be provided for that class. Rather than defining it as a permission. In Java, this permission is provided by implementing the interface java.io.Serializable. This interface does not have any abstract methods. Purpose of this interface is just indicating that, the object of this classes are allowed to convert into a stream of bytes.

Even though, there are no methods to override, it is the best practice to add a long variable serialVersionUID to the Serializable class. This id is used to identify the exact class of byte stream, when the stream is converted back to the Java object. The serialVersionUID can be any long number.
import java.io.Serializable;

/**
 * Student class
 *
 * @author gobinath
 *
 */
public class Student implements Serializable {
    // Serial version ID – Just a random number
    private static final long serialVersionUID = 5230549922091722630L;

    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getAge() {
        return age;
    }
}
Now run the code again and test the output. Fine! we have converted a Student object into an array of bytes. What about converting the byte array back to Student object?

Add a new method toStudent in the Main class. This method converts the given array of bytes to a Student object.
/**
 * Convert stream of bytes to Student.
 *
 * @param stream
 *            byte array
 * @return Student object
 */
public static Student toStudent(byte[] stream) {
    Student stu = null;

    try (ByteArrayInputStream bais = new ByteArrayInputStream(stream);
            ObjectInputStream ois = new ObjectInputStream(bais);) {
        stu = (Student) ois.readObject();
    } catch (IOException e) {
        // Error in de-serialization
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        // You are converting an invalid stream to Student
        e.printStackTrace();
    }
    return stu;
}
Modify the Main class as shown below and test the output.
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;

public class Main {

    public static void main(String[] args) {
        Student stu = createStudent();
        System.out.println("Name: " + stu.getName());
        System.out.println("Age: " + stu.getAge());

        // Convert the object to stream
        byte[] stream = toStream(stu);

        // Print the array
        System.out.println(Arrays.toString(stream));

        Student convertedStu = toStudent(stream);
        System.out.println("Name: " + convertedStu.getName());
        System.out.println("Age: " + convertedStu.getAge());
    }

    /**
     * Create a sample Student object.
     *
     * @return a Student object.
     */
    public static Student createStudent() {
        // Create a Student object
        Student stu = new Student();
        stu.setName("Alice");
        stu.setAge(24);

        return stu;
    }

    /**
     * Convert a Student object into stream of bytes.
     *
     * @param stu
     *            Student object.
     * @return stream of bytes
     */
    public static byte[] toStream(Student stu) {
        // Reference for stream of bytes
        byte[] stream = null;
        // ObjectOutputStream is used to convert a Java object into OutputStream
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(baos);) {
            oos.writeObject(stu);
            stream = baos.toByteArray();
        } catch (IOException e) {
            // Error in serialization
            e.printStackTrace();
        }
        return stream;
    }

    /**
     * Convert stream of bytes to Student.
     *
     * @param stream
     *            byte array
     * @return Student object
     */
    public static Student toStudent(byte[] stream) {
        Student stu = null;

        try (ByteArrayInputStream bais = new ByteArrayInputStream(stream);
                ObjectInputStream ois = new ObjectInputStream(bais);) {
            stu = (Student) ois.readObject();
        } catch (IOException e) {
            // Error in de-serialization
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // You are converting an invalid stream to Student
            e.printStackTrace();
        }
        return stu;
    }

}
The above code serializes a Student object to a stream of bytes, and deserailizes it back to the Student object. Let's use serialization to save a Student object to a file and read it back later.

Create the save method and call this method from the main method as shown below. Remember to change the file location based on your operating system.
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Main {

    public static void main(String[] args) {
        Student stu = createStudent();
        System.out.println("Name: " + stu.getName());
        System.out.println("Age: " + stu.getAge());

        // Give any filename
        save(stu, "/home/gobinath/student.xxx");
    }

    /**
     * Create a sample Student object.
     *
     * @return a Student object.
     */
    public static Student createStudent() {
        // Create a Student object
        Student stu = new Student();
        stu.setName("Alice");
        stu.setAge(24);

        return stu;
    }

    /**
     * Save a student into a file using Serialization.
     *
     * @param stu
     *            the Student to save.
     * @param fileName
     *            the location to save.
     */
    public static void save(Student stu, String fileName) {
        try (FileOutputStream fos = new FileOutputStream(fileName);
                ObjectOutputStream oos = new ObjectOutputStream(fos);) {
            oos.writeObject(stu);
        } catch (FileNotFoundException e) {
            // Error in accessing the file
            e.printStackTrace();
        } catch (IOException e) {
            // Error in converting the Student
            e.printStackTrace();
        }
    }
}
After executing this code, go and check whether the file is created or not. If you do not have any permission issues for the given valid location, the file must be created at the provided location.

To read the Student object, add a new method read as shown below and call it from the main method.
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 Main {

    public static void main(String[] args) {
        Student stu = createStudent();
        System.out.println("Name: " + stu.getName());
        System.out.println("Age: " + stu.getAge());

        // Give any filename
        save(stu, "/home/gobinath/student.xxx");

        // Provide the file name of your serialized object
        Student newStu = read("/home/gobinath/student.xxx");
        System.out.println("Name: " + newStu.getName());
        System.out.println("Age: " + newStu.getAge());
    }

    /**
     * Create a sample Student object.
     *
     * @return a Student object.
     */
    public static Student createStudent() {
        // Create a Student object
        Student stu = new Student();
        stu.setName("Alice");
        stu.setAge(24);

        return stu;
    }

    /**
     * Save a student into a file using Serialization.
     *
     * @param stu
     *            the Student to save.
     * @param fileName
     *            the location to save.
     */
    public static void save(Student stu, String fileName) {
        try (FileOutputStream fos = new FileOutputStream(fileName);
                ObjectOutputStream oos = new ObjectOutputStream(fos);) {
            oos.writeObject(stu);
        } catch (FileNotFoundException e) {
            // Error in accessing the file
            e.printStackTrace();
        } catch (IOException e) {
            // Error in converting the Student
            e.printStackTrace();
        }
    }

    /**
     * Reading Student object from the given file.
     *
     * @param fileName
     *            location of the file.
     * @return converted Student object.
     */
    public static Student read(String fileName) {
        Student stu = null;
        try (FileInputStream fis = new FileInputStream(fileName); ObjectInputStream ois = new ObjectInputStream(fis);) {
            stu = (Student) ois.readObject();
        } catch (FileNotFoundException e) {
            // Error in accessing the file
            e.printStackTrace();
        } catch (IOException e) {
            // Error in converting the Student
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // You are converting an invalid stream to Student
            e.printStackTrace();
        }
        return stu;
    }
}
This code will read and print the saved student's name Alice, and her age 24.

Seems to be good. But what will be the output, if the saved object is read after more than one years? Regardless of number of years, the age will be displayed as 24 forever. A possible trick to overcome this problem is: rather than saving the age, save the birth year to the stream of bytes and redefine the age when deserailize the student back. To ignore the serialization of a property, declare the property as a transient property. Modify the int age as transient int age and run the code.
import java.io.Serializable;

/**
 * Student class
 *
 * @author gobinath
 *
 */
public class Student implements Serializable {
    // Serial version ID – Just a random number
    private static final long serialVersionUID = 5230549922091722630L;

    private String name;
    private transient int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getAge() {
        return age;
    }
}
This time the deserialized student's age will be 0. This is because of the transient modifier. If the age is not available during deserialization, Java will assign the default value of that data type (For int, default value is 0).

To save the birth year, add the following method into the Student class.
/**
 * This method is called during serialization.
 *
 * @param oos
 */
private void writeObject(ObjectOutputStream oos) {
    // Create a calendar object of current date
    Calendar current = Calendar.getInstance();
    // Get the current year
    int currentYear = current.get(Calendar.YEAR);
    // Calculate the birth year
    int birthYear = currentYear - age;

    try {
        // Write the default attributes first
        oos.defaultWriteObject();
        // Write the birth year
        oos.writeInt(birthYear);
    } catch (IOException e) {
        e.printStackTrace();
    }
}
The writeObject method is called by Java, when the object is serialized to stream of bytes.

To restore the age, add the following method into the Student class.
/**
 * This method is called during deserialization.
 *
 * @param ois
 */
private void readObject(ObjectInputStream ois) {
    // Create a calendar object of current date
    Calendar current = Calendar.getInstance();
    // Get the current year
    int currentYear = current.get(Calendar.YEAR);

    try {
        // Read the default attributes first
        ois.defaultReadObject();
        // Read the birth year
        int birthYear = ois.readInt();
        // Calculate the age
        this.age = currentYear - birthYear;
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}
The readObject method is called by Java, when a stream of byte is deserialized to Java object.

After the modification, your code should be something similar to this:
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Calendar;

/**
 * Student class
 *
 * @author gobinath
 *
 */
public class Student implements Serializable {
    // Serial version ID
    private static final long serialVersionUID = 5230549922091722630L;

    private String name;
    private transient int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getAge() {
        return age;
    }

    /**
     * This method is called during serialization.
     *
     * @param oos
     */
    private void writeObject(ObjectOutputStream oos) {
        // Create a calendar object of current date
        Calendar current = Calendar.getInstance();
        // Get the current year
        int currentYear = current.get(Calendar.YEAR);
        // Calculate the birth year
        int birthYear = currentYear - age;

        try {
            // Write the default attributes first
            oos.defaultWriteObject();
            // Write the birth year
            oos.writeInt(birthYear);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * This method is called during deserialization.
     *
     * @param ois
     */
    private void readObject(ObjectInputStream ois) {
        // Create a calendar object of current date
        Calendar current = Calendar.getInstance();
        // Get the current year
        int currentYear = current.get(Calendar.YEAR);

        try {
            // Read the default attributes first
            ois.defaultReadObject();
            // Read the birth year
            int birthYear = ois.readInt();
            // Calculate the age
            this.age = currentYear - birthYear;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
This time, output will be updated based on the current year.

Conclusively serialization is the process of converting an object into stream of bytes. The converted byte stream can be used for multiple purposes. Java has a marker interface (an interface without any methods) Serializable to flag the classes, that their objects are allowed to undergo the serialization. writeObject and readObject methods are used to perform advanced operations during serialization and deserialization.

Find the source codes at Git Hub.
Previous
Next Post »

Contact Form

Name

Email *

Message *