What is Java Serialization?
Serialization is the process used to convert an object state into stream of bytes so that it can be written into a file, transported through a network or stored into database. De-serialization is just a vice versa.
Why do we need Serialization in Java?
We need Serialization for the following reasons:
- Save object state into a file
- State of an object is available in JVM until it’s scope, If we need to hold state of an object even after it’s scope then we could serialize object state into a file on the local disk and de-serialize when ever we need.
- Transfer object state
- If we would like to transfer the state of an object across multiple JVM’s, we could serialize and transfer from one JVM to another JVM in distributed applications
- Save object state into database
- If we would like to save state of an object permanently, then we could serialize and save into database so that it can be retrieved later
- Clone of an object
- If we want an exact copy (deep copy) of an object, then we can serialize the object to a stream of bytes, and then de-serializing it

How to make a java class serializable?
A class must implement either java.io.Serializable or java.io.Externalizable interface to be eligible for serialization. java.io.Serializable is a marker interface that means it doesn’t contains any methods or fields.
If we try to serialize a non-serializable object, then we hit with an exception called java.io.NotSerializableException.
What is the extension of serialized file ?
Even other extensions are allowed, The .ser extension is preferred to be used for files representing serialized objects
transient keyword
transient modifier is applicable only for variables. transient variables not serialized during serialization process. During de-serialization process transient variables will be initialized with default value. (e.g: for objects it is null, for int it is 0).
static vs transient
A static variable is not part of an object state, hence static variables not serialized during serialization process. Due to this there is no need of declaring static variable as transient. During de-serialization process static variables will be assigned with current value defined in the loaded class.
Java Serialization with Aggregation (has-a Relationship)
A serializable class should have all serializable references. If we try to serialize a serializable object which has non-serializable references, then we will hit with an exception called java.io.NotSerializableException.
Java Serialization with array or collection
All primitive arrays and collections(list/set/map … etc.) will be serialized and de-serialized successfully. All user-defined object arrays and collections will get serialized and de-serialized successfully if user-defined object is serializable Otherwise we will hit with an exception called java.io.NotSerializableException.
How to do Serialization and Deserialization ?
First we need to define a domain class which implements Serializable marker interface with it’s attributes. Here in our example we are defining Employee class with serialVersionUID, and two instance variables, final variable, static variable, transient variable, constructors, setter & getters, toString() method as shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
package com.vidvaan.corejava.serilaization01.serilizable; import java.io.Serializable; // If we implement Serializable, then we can convert this class instance into stream of bytes public class Employee implements Serializable { // This serialVersionUID field is to verify that the saved and loaded objects // have the same attributes private static final long serialVersionUID = 2L; // instance variables get serialized // deserialization assign the serialized value private int employeeId; private String name; // transient variables will not be serialized // deserialization assign default value, here it is 0.0 private transient double salary; // static variables will not be serialized // Since static variables belongs to the class. After de-serialization they get // their current values from the loaded class. private static String designation; // final variables will get serialized // deserialization process do not invoke the constructor, but still // deserialization process assign value to this final private variable private final String department; // Constructor won't be invoked in the deserialization public Employee(int employeeId, String name, double salary, String designation, String department) { System.out.println("------------Contructor------------"); this.employeeId = employeeId; this.name = name; this.salary = salary; Employee.designation = designation;// Since it is static,it is not part of Object this.department = department; } @Override public String toString() { return "Employee [employeeId=" + employeeId + ", name=" + name + ", salary=" + salary + ", designation=" + designation + ", department=" + department + "]"; } // Setters & Getters } |
In the above example, comments explain each variable and their behavior clearly.
To serialize and de-serialize objects, Java API has given ObjectInputStream and ObjectOutputStream high level classes that extend java.io.InputStream and java.io.OutputStream respectively. ObjectOutputStream can write primitive types and graphs of objects to an OutputStream as a stream of bytes. These streams can subsequently read back using ObjectInputStream.
The most important method in ObjectOutputStream is:
1 |
public final void writeObject(Object o) throws IOException; |
Which takes a serializable object and converts it into a stream of bytes. Similarly, the most important method in ObjectInputStream is:
1 |
public final Object readObject() throws IOException, ClassNotFoundException; |
Which can read a stream of bytes and convert it back into a Java object. This can then be cast back to the original object.
The following example code we used to serialize and de-serialize Employee domain object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
package com.vidvaan.corejava.serilaization01.serilizable; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class SerializationAndDeserializationExample { public static void main(String[] args) throws IOException, ClassNotFoundException { Employee empObj = new Employee(1001, "Sekhar Reddy", 99999, "Architect", "IT"); System.out.println("Before serialization => " + empObj.toString()); // Serialization serialize(empObj); // Deserialization Employee deserialisedEmpObj = deserialize(); System.out.println("After deserialization => " + deserialisedEmpObj.toString()); } // Serialization code private static void serialize(Employee empObj) throws IOException { try (FileOutputStream fos = new FileOutputStream("Employee.ser"); ObjectOutputStream oos = new ObjectOutputStream(fos)) { oos.writeObject(empObj); } } // Deserialization code private static Employee deserialize() throws IOException, ClassNotFoundException { try (FileInputStream fis = new FileInputStream("Employee.ser"); ObjectInputStream ois = new ObjectInputStream(fis)) { return (Employee) ois.readObject(); } } } |
Output : When we run the above program, it will create a file “Employee.ser” in the project directory. The same will be loaded in the de-serialization process.
————Contructor————
Before serialization => Employee [employeeId=1001, name=Sekhar Reddy, salary=99999.0, designation=Architect, department=IT]
After deserialization => Employee [employeeId=1001, name=Sekhar Reddy, salary=0.0, designation=Architect, department=IT]
As per the above output,
- We can see that instance variables employeeId, name are successfully serialized and de-serialized.
- The transient variable salary was not serialized that’s why in de-serialization transient variable salary was assigned with default value 0.0.
- The final variable department was are successfully serialized and de-serialized.
- The static variable designation also seems like successfully serialized and de-serialized. But it is not the truth. Let’s run the following deserialization logic shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package com.vidvaan.corejava.serilaization01.serilizable; import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; public class DeserializationExample { public static void main(String[] args) throws IOException, ClassNotFoundException { // Deserialization Employee deserialisedEmpObj = deserialize(); System.out.println("After deserialization => " + deserialisedEmpObj.toString()); } // Deserialization code private static Employee deserialize() throws IOException, ClassNotFoundException { try (FileInputStream fis = new FileInputStream("Employee.ser"); ObjectInputStream ois = new ObjectInputStream(fis)) { return (Employee) ois.readObject(); } } } |
Output :
After deserialization => Employee [employeeId=1001, name=Sekhar Reddy, salary=0.0, designation=null, department=IT]
Surprise !!! As per the above output, static variable not serialized and de-serialized. But in the previous example, static variable value was assigned with current value defined in the loaded class during de-serialization. So static variables are not serialized and de-serialized.
final vs transient
Only final variable are will get serialized. But final transient variables will not get serialized, But while de-serializing, final-transient variable explicitly initialized, the value is re-affected. We will understand final transient behavior with the following example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
package com.vidvaan.corejava.serilaization03.FinalVsTransient; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Arrays; import java.util.List; //All final transient variables will not get serialized, But while de-serializing, // final-transient variable explicitly initialized, the value is re-affected public class FinalVsTransientDemo implements Serializable { // This serialVersionUID field is to verify that the saved and loaded objects // have the same attributes private static final long serialVersionUID = 1L; // initialized final transient variable will be reaffected when deserialized private final transient String initializedVariable = "Vidvaan"; // declared final transient variable will be set to null when deserialized private final transient String declarationVariable; // dynamic expression final transient variable will be set to null when // deserialized private final transient String dynamicExpressionVariable = new String("SomaSekharReddy"); // constant expression final transient variable will be reaffected when // deserialized private final transient String constantExpressionVariable = initializedVariable + initializedVariable + 1; // constant expression final transient variable will be reaffected when // deserialized private final transient int intConstantExpressionVariable = 1 * 2 * 3; // dynamic expression final transient variable will be set to null when // deserialized private final transient int intDynamicExpressionVariable = Integer.valueOf(123); // dynamic expression final transient variable will be set to null when // deserialized private final transient int[] arrayVariable = { 1, 2, 3 }; // dynamic expression final transient variable will be set to null when // deserialized private final transient List<String> listVariable = Arrays.asList("Sekhar", "Reddy"); // Constructor won't be invoked in the deserialization public FinalVsTransientDemo() { System.out.println("--------------Constructor---------------"); declarationVariable = "Vidvaan"; } public static void main(String[] args) throws IOException, ClassNotFoundException { FinalVsTransientDemo finalVsTransientDemo = new FinalVsTransientDemo(); System.out.println("Before serialization => " + finalVsTransientDemo.toString()); // Serialization serialize(finalVsTransientDemo); // Deserialization FinalVsTransientDemo obj = deserialize(); System.out.println("After deserialization => " + obj.toString()); } // Serialization code private static void serialize(FinalVsTransientDemo obj) throws IOException { try (FileOutputStream fos = new FileOutputStream("FinalVsTransientDemo.ser"); ObjectOutputStream oos = new ObjectOutputStream(fos)) { oos.writeObject(obj); } } // Deserialization code private static FinalVsTransientDemo deserialize() throws IOException, ClassNotFoundException { try (FileInputStream fis = new FileInputStream("FinalVsTransientDemo.ser"); ObjectInputStream ois = new ObjectInputStream(fis)) { return (FinalVsTransientDemo) ois.readObject(); } } @Override public String toString() { return "FinalVsTransientDemo [initializedVariable=" + initializedVariable + ", declarationVariable=" + declarationVariable + ", dynamicExpressionVariable=" + dynamicExpressionVariable + ", constantExpressionVariable=" + constantExpressionVariable + ", intConstantExpressionVariable=" + intConstantExpressionVariable + ", intDynamicExpressionVariable=" + intDynamicExpressionVariable + ", arrayVariable=" + Arrays.toString(arrayVariable) + ", listVariable=" + listVariable + "]"; } } |
Output :
————–Constructor—————
Before serialization => FinalVsTransientDemo [initializedVariable=Vidvaan, declarationVariable=Vidvaan, dynamicExpressionVariable=SomaSekharReddy, constantExpressionVariable=VidvaanVidvaan1, intConstantExpressionVariable=6, intDynamicExpressionVariable=123, arrayVariable=[1, 2, 3], listVariable=[Sekhar, Reddy]]
After deserialization => FinalVsTransientDemo [initializedVariable=Vidvaan, declarationVariable=null, dynamicExpressionVariable=null, constantExpressionVariable=VidvaanVidvaan1, intConstantExpressionVariable=6, intDynamicExpressionVariable=0, arrayVariable=null, listVariable=null]
In the above example, During de-serialization, final-transient declared variable is set to default values. final-transient initialized variables re-affected, final-transient constant expressions re-affected, final-transient dynamic expressions set to default values.
We can assign values to final fields in constructors only and de-serialization process do not invoke any constructor but still it can assign values to final fields.
Java Serialization with Inheritance(is-a Relationship)
If parent class is serializable class, then by default child class is serializable. So when we serialize child class object, then both parent and child class instance variables will get serialized. In de-serialization also both parent and child class instance variables will get de-serialized successfully. Let’s see an example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
package com.vidvaan.corejava.serilaization04.inheritance; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; // Since Parent is Serializable then both parentVariables and childVariables gets serialized and de-serialized class Parent implements Serializable { private static final long serialVersionUID = 1L; private String parentVariable; public Parent(String parentVariable) { System.out.println("-------------Parent Constructor-------------"); this.parentVariable = parentVariable; } public String getParentVariable() { return parentVariable; } public void setParentVariable(String parentVariable) { this.parentVariable = parentVariable; } } // By inheritance as parent Serializable, Then Child too Serializable class Child extends Parent { private static final long serialVersionUID = 1L; private String childVariable; public Child(String childVariable, String parentVariable) { super(parentVariable); System.out.println("-------------Child Constructor-------------"); this.childVariable = childVariable; } public String getChildVariable() { return childVariable; } public void setChildVariable(String childVariable) { this.childVariable = childVariable; } } public class ParentClassSerializableDemo { public static void main(String[] args) throws IOException, ClassNotFoundException { Child child = new Child("Vidvaan-Child", "Vidvaan-Parent"); System.out.println("-------------Before serialization-------------"); System.out.println("parentVariable = " + child.getParentVariable()); System.out.println("childVariable = " + child.getChildVariable()); // Serialization serialize(child); // Deserialization Child obj = deserialize(); System.out.println("-------------After deserialization-------------"); System.out.println("parentVariable = " + obj.getParentVariable()); System.out.println("childVariable = " + obj.getChildVariable()); } // Serialization code private static void serialize(Child obj) throws IOException { try (FileOutputStream fos = new FileOutputStream("Child.ser"); ObjectOutputStream oos = new ObjectOutputStream(fos)) { oos.writeObject(obj); } } // Deserialization code private static Child deserialize() throws IOException, ClassNotFoundException { try (FileInputStream fis = new FileInputStream("Child.ser"); ObjectInputStream ois = new ObjectInputStream(fis)) { return (Child) ois.readObject(); } } } |
Output :
————-Parent Constructor————-
————-Child Constructor————-
————-Before serialization————-
parentVariable = Vidvaan-Parent
childVariable = Vidvaan-Child
————-After deserialization————-
parentVariable = Vidvaan-Parent
childVariable = Vidvaan-Child
If parent class is not serializable but only child class implements serializable, Then when we serialize child class object, Only child class instance variables will get serialized but parent class instance variables will not get serialized. In de-serialization only child class instance variables will get de-serialized, but parent instance variables assigned with default values. Let’s see an example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
package com.vidvaan.corejava.serilaization04.inheritance; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable;; //If SuperClass is not Serializable then parentVariables will not get serialized and de-serialized class SuperClass { // This variable will not get serialized and de-serialized private String parentVariable; // This constructor is mandatory, because in the de-serialization it will be // invoked public SuperClass() { System.out.println("-------------SuperClass No Argument Constructor-------------"); } public SuperClass(String parentVariable) { System.out.println("-------------SuperClass Parameterized Constructor-------------"); this.parentVariable = parentVariable; } public String getParentVariable() { return parentVariable; } public void setParentVariable(String parentVariable) { this.parentVariable = parentVariable; } } //If only SubClass is Serializable, then only childVariables will get serialized and de-serialized class SubClass extends SuperClass implements Serializable { private static final long serialVersionUID = 1L; // This variable will get serialized and de-serialized private String childVariable; public SubClass(String childVariable, String parentVariable) { super(parentVariable); System.out.println("-------------SubClass Constructor-------------"); this.childVariable = childVariable; } public String getChildVariable() { return childVariable; } public void setChildVariable(String childVariable) { this.childVariable = childVariable; } } public class OnlyChildClassSerializableDemo { public static void main(String[] args) throws Exception { SubClass subClass = new SubClass("Vidvaan-Child", "Vidvaan-Parent"); System.out.println("-------------Before serialization-------------"); System.out.println("parentVariable = " + subClass.getParentVariable()); System.out.println("childVariable = " + subClass.getChildVariable()); // Serialization serialize(subClass); // Deserialization SubClass obj = deserialize(); System.out.println("-------------After deserialization-------------"); System.out.println("parentVariable = " + obj.getParentVariable()); System.out.println("childVariable = " + obj.getChildVariable()); } // Serialization code private static void serialize(SubClass obj) throws IOException { try (FileOutputStream fos = new FileOutputStream("SubClass.ser"); ObjectOutputStream oos = new ObjectOutputStream(fos)) { oos.writeObject(obj); } } // Deserialization code private static SubClass deserialize() throws IOException, ClassNotFoundException { try (FileInputStream fis = new FileInputStream("SubClass.ser"); ObjectInputStream ois = new ObjectInputStream(fis)) { return (SubClass) ois.readObject(); } } } |
Output :
————-SuperClass Parameterized Constructor————-
————-SubClass Constructor————-
————-Before serialization————-
parentVariable = Vidvaan-Parent
childVariable = Vidvaan-Child
————-SuperClass No Argument Constructor————-
————-After deserialization————-
parentVariable = null
childVariable = Vidvaan-Child
As per the above output, In the de-serialization process, Interesting thing is super class default contractor is called, If we don’t define default constructor in super class, then we will hit with an exception.
What is the serialVersionUID?
SerialVersionUID
is an id, which acts as a version number for each serializable class. It is used to ensure that during de-serialization the same class is loaded, which was used during serialization.
If we don’t give serialVersionUID in serializable class, JVM generates one for our class, based on class structure(attributes, types, access modifiers). In General when we change the class structure, then JVM generates different serialVersionUID. There is a chance that, different JVM’s(different vendors, different versions, different platform) generates different serialVersionUID even for the same class.
Suppose, when we are sending object state from one application to another application which are running on different JVM’s(different vendor JVM’s or different platform JVM’s or different version JVM’s) then serialVersionUID of both sender and receiver classes should be same. Otherwise de-serialization will be failed.
In serialization, when we serialize object state, along with object state serialVersionUID also get serialized, which will be used in the de-serialization process.
In de-serialization, JVM reads serialVersionUID from serialized file and compares with loaded class serialVersionUID. If they don’t match then we will get an exception called InvalidClassException.
So it is always recommended that each Serializable class should explicitly declare its serialVersionUID.
Let’s see an example to demonstrate serialVersionUID.
Person.java : Here we are not declaring serialVersionUID.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
package com.vidvaan.corejava.serilaization06.SerialVersionUID; import java.io.Serializable; public class Person implements Serializable { // If we don't add this, and run SerializeSerialVersionUIDExample then add new // attribute to this class and then run DeserializeSerialVersionUIDExample, then // we hit with java.io.InvalidClassException // private static final long serialVersionUID = 1L; private String firstName; private String lastName; // Try to add new variable, then run DeserializeSerialVersionUIDExample // private String newVariable; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } @Override public String toString() { return "Person [firstName=" + firstName + ", lastName=" + lastName + "]"; } } |
SerializeSerialVersionUIDExample.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
package com.vidvaan.corejava.serilaization06.SerialVersionUID; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; public class SerializeSerialVersionUIDExample { public static void main(String[] args) throws IOException, ClassNotFoundException { Person person = new Person(); person.setFirstName("Sekhar Reddy"); person.setLastName("Yerragudi"); System.out.println("Before serialization => " + person.toString()); // Serialization serialize(person); } // Serialization code private static void serialize(Person obj) throws IOException { try (FileOutputStream fos = new FileOutputStream("Person.ser"); ObjectOutputStream oos = new ObjectOutputStream(fos)) { oos.writeObject(obj); } } } |
Output :
Before serialization => Person [firstName=Sekhar Reddy, lastName=Yerragudi]
Now in Person.java, uncomment linenumber:15 to add new attribute called ‘newVariable’. Then run the following de-serialization example.
DeserializeSerialVersionUIDExample.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package com.vidvaan.corejava.serilaization06.SerialVersionUID; import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; public class DeserializeSerialVersionUIDExample { public static void main(String[] args) throws IOException, ClassNotFoundException { // Deserialization Person obj = deserialize(); System.out.println("After deserialization => " + obj.toString()); } // Deserialization code private static Person deserialize() throws IOException, ClassNotFoundException { try (FileInputStream fis = new FileInputStream("Person.ser"); ObjectInputStream ois = new ObjectInputStream(fis)) { return (Person) ois.readObject(); } } } |
Output :
Exception in thread “main” java.io.InvalidClassException: com.vidvaan.corejava.serilaization06.SerialVersionUID.Person; local class incompatible: stream classdesc serialVersionUID = 8304851845376733956, local class serialVersionUID = 1072717160948856604
As per the above output, we can understand that, when we added new variable to the Person class, JVM has created new serialVersionUID. In our case we may have the requirement that, for the new variable JVM would assign default value in the de-serialization process. But it will fail if we don’t give serialVersionUID. Now re-run above examples in the same sequence by giving serialVersionUID in Person class. Then it will run de-serialization successfully even after adding new attribute. Hence it is always recommended to give serialVersionUID explicitly for all serializable classes.
Customizing Serialization and Deserialization?
Java has given the default way in which objects can be serialized and de-serialized. But we can customize the serialization process in our serializable class by override the default behavior. Custom serialization particularly useful when we are trying to serialize an object that has some non-serializable attributes. This can be done by providing two methods, writeObject
and readObject
, inside the class that we want to serialize.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// Custom serialization logic: // We can do additional serialization logic on top of the default one e.g. // serialize some extra variables which are not by default serialized private void writeObject(ObjectOutputStream oos) throws IOException { // Any custom logic oos.defaultWriteObject(); // Calling the default serialization logic // Any custom logic } // Custom de-serialization logic: // We can do additional serialization logic on top of the default one e.g. // serialize some extra variables which are not by default serialized private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { // Any custom logic ois.defaultReadObject(); // Calling the default de-serialization logic // Any custom logic } |
These methods always should be private, because these are not meant for any one to call explicitly. Even these methods are private, Still they can be called by JVM in the serialization and de-serialization process. Let’s see an example to customize serialization and de-serialization as shown below.
Address.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
package com.vidvaan.corejava.serilaization02.customized; public class Address { private int houseNumber; private String street; private String city; private String zipCode; public Address(int houseNumber, String street, String city, String zipCode) { super(); this.houseNumber = houseNumber; this.street = street; this.city = city; this.zipCode = zipCode; } @Override public String toString() { return "Address [houseNumber=" + houseNumber + ", street=" + street + ", city=" + city + ", zipCode=" + zipCode + "]"; } public int getHouseNumber() { return houseNumber; } public void setHouseNumber(int houseNumber) { this.houseNumber = houseNumber; } public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getZipCode() { return zipCode; } public void setZipCode(String zipCode) { this.zipCode = zipCode; } } |
Student.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
package com.vidvaan.corejava.serilaization02.customized; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class Student implements Serializable { private static final long serialVersionUID = 1L; private int studentNumber; private String studentName; // This will not get serialized by default, it should be transient private transient Address address; // constructor won't be invoked when deserialized public Student(int studentNumber, String studentName, Address address) { System.out.println("-------------Constructor-------------"); this.studentNumber = studentNumber; this.studentName = studentName; this.address = address; } // Custom serialization logic: // We can do additional serialization logic on top of the default one e.g. // serialize some extra variables which are not by default serialized private void writeObject(ObjectOutputStream oos) throws IOException { System.out.println("-------------Custom serialization logic-------------"); oos.defaultWriteObject(); // Calling the default serialization logic // As address is transient it will not be serialized by default // If we really need to serialize address data we could do as follows oos.writeObject(this.address.getHouseNumber()); oos.writeObject(this.address.getStreet()); oos.writeObject(this.address.getCity()); } // Custom deserialization logic: // We can do additional serialization logic on top of the default one e.g. // serialize some extra variables which are not by default serialized private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { System.out.println("-------------Custom deserialization logic-------------"); ois.defaultReadObject(); // Calling the default deserialization logic // Here we are deserialize each variable in the order they serialized Integer houseNumber = (Integer) ois.readObject(); String street = (String) ois.readObject(); String city = (String) ois.readObject(); // Here we are creating Address object and setting state manually this.address = new Address(houseNumber, street, city, null); } @Override public String toString() { return "Student [studentNumber=" + studentNumber + ", studentName=" + studentName + ", address=" + address + "]"; } } |
CustomSerializationAndDeserializationExample.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
package com.vidvaan.corejava.serilaization02.customized; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class CustomSerializationAndDeserializationExample { public static void main(String[] args) throws IOException, ClassNotFoundException { Address address = new Address(11199, "Seward Plaza", "Omaha", "68154"); Student student = new Student(1001, "Sekhar Reddy", address); System.out.println("Before s erialization => " + student.toString()); // Serialization serialize(student); // Deserialization Student obj = deserialize(); System.out.println("After deserialization => " + obj.toString()); } // Serialization code private static void serialize(Student obj) throws IOException { try (FileOutputStream fos = new FileOutputStream("Student.ser"); ObjectOutputStream oos = new ObjectOutputStream(fos)) { oos.writeObject(obj); } } // Deserialization code private static Student deserialize() throws IOException, ClassNotFoundException { try (FileInputStream fis = new FileInputStream("Student.ser"); ObjectInputStream ois = new ObjectInputStream(fis)) { return (Student) ois.readObject(); } } } |
Output :
————-Constructor————-
Before s erialization => Student [studentNumber=1001, studentName=Sekhar Reddy, address=Address [houseNumber=11199, street=Seward Plaza, city=Omaha, zipCode=68154]]
————-Custom serialization logic————-
————-Custom deserialization logic————-
After deserialization => Student [studentNumber=1001, studentName=Sekhar Reddy, address=Address [houseNumber=11199, street=Seward Plaza, city=Omaha, zipCode=null]]
In the above example, we have serialized Address (non-serializable) with custom serialization. Note that we should mark the non-serializable attributes as transient to avoid the NotSerializableException. Note that, in the de-serialization we need to extract serialized variables in the same exact order in which they were serialized.
How to prevent serialization and de-serialization ?
We can prevent serialization and de-serialization of class which has got the serialization capability. Some times our class may got serialization capability from parent class, But we don’t want to allow our class to be serialized and de-serialized. To do so in our class we need to add following two private methods.
1 2 3 4 5 6 7 8 9 10 11 12 |
// this method will get invoked while serialization, but we will throw exception // to prevent serialization private void writeObject(ObjectOutputStream oos) throws IOException { throw new NotSerializableException("Animal object not allowed for serialization"); } // this method will get invoked while de-serialization, but we will throw // exception to prevent de-serialization private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { throw new NotSerializableException("Animal object not allowed for de-serialization"); } |
The above methods will be called while serialization and de-serialization. In these methods we are throwing exception NotSerializableException. Let’s see an example below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
package com.vidvaan.corejava.serilaization05.prevent; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.NotSerializableException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; // We can prevent serialization and de-serialization of class which has got the serialization capability may be from its parent class. public class PreventSerializationAndDeserializationDemo { public static void main(String[] args) throws IOException, ClassNotFoundException { Animal animal = new Animal("Tiger"); System.out.println("Before serialization => " + animal.toString()); // Serialization serialize(animal); // Deserialization Animal obj = deserialize(); System.out.println("After deserialization => " + obj.toString()); } // Serialization code private static void serialize(Animal obj) throws IOException { try (FileOutputStream fos = new FileOutputStream("Animal.ser"); ObjectOutputStream oos = new ObjectOutputStream(fos)) { oos.writeObject(obj); } } // Deserialization code private static Animal deserialize() throws IOException, ClassNotFoundException { try (FileInputStream fis = new FileInputStream("Animal.ser"); ObjectInputStream ois = new ObjectInputStream(fis)) { return (Animal) ois.readObject(); } } } // We can prevent serialization and de-serialization, By just providing by providing two methods, writeObject and readObject and throw some exception class Animal implements Serializable { private static final long serialVersionUID = 1L; private String name; public Animal(String name) { this.name = name; } // this method will get invoked while serialization, but we will throw exception // to prevent serialization private void writeObject(ObjectOutputStream oos) throws IOException { throw new NotSerializableException("Animal object not allowed for serialization"); } // this method will get invoked while de-serialization, but we will throw // exception to prevent de-serialization private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { throw new NotSerializableException("Animal object not allowed for de-serialization"); } @Override public String toString() { return "Animal [name=" + name + "]"; } } |
Output :
Before serialization => Animal [name=Tiger]
Exception in thread “main” java.io.NotSerializableException: Animal object not allowed for serialization
Why java.lang.Object not implementing Serializable?
Because, what if we do NOT want to make certain fields as Serializable(may be they are holding some credentials information) and by mistake we missed to add transient to those fields? Then it will be a potential security hole.
We may not want to serialize all the objects. Even serializing some objects like Thread and de-serializing them in another JVM does not make any sense.
Serialization using Externalizable interface
The usage of Java Externalizable interface is clearly explained here.