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
- Steps for object Serialization in Java
- Steps for object Deserialization in Java
- Use of serialization in Java
- Java Serialization and Deserialization example
- Implementing writeObject() and readObject() methods
- writeObject() and readObject() methods - Serialization with inheritance
- Portability with Java serialization
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-
- Transmitted from one JVM to another where the object can be reconstituted using those bytes and used in further processing.
- Stored on a disk/DB for further use.
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
- One thing to be careful about here is that the order of reading the fields should be same as the order they were written.
- 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
You may also like-