Follow this simple tutorial to understand how serialization works in Java. Also, we’ll explain how to implement it in your Java projects. To help you, we have added a few code examples and a sample project.
Besides knowing about serialization in Java it’s even more important to learn the different ways to implement serialization. We’ve also addressed them in this post.
Simple Steps to Learn Serialization in Java
Serialization is a way of converting data structures or the state of the object in a format that can be preserved and retrieved as and when required. The process of reforming the object from the decomposed state is called deserialization. Today most of the object-oriented languages (including Java) provide native support for serialization and deserialization.
Next, you will see us address some of the very basic concepts of Java serialization. It’ll surely increase your knowledge of Java serialization topics.
Must Read: 30 Java Coding Questions Every Programmer Should Try
The Concept of Serialization in Java
Java serialization is the process of converting an object into a stream of bytes or byte arrays. The byte array represents the class of the object, the version of the object, and the internal state of the object.
Deserialization in Java
Deserialization is the process of rebuilding the byte stream into a live Java Object that must be usable.
The Purpose of Java Serialization
You can use Java serialization to perform the following tasks.
Stashing
Rather than holding a large object in memory, it’s better to cache it to a local file via serialization. For your note, if you attempt to save a non-serializable object, the JVM will fail the operation with <NotSerializableException>.
Data Transmission
Java permits serializing an object over a network using RMI (Remote Method Invocation), a distributed technology of Java. RMI enables a Java client object to communicate with the instance of a Java server hosted on a remote system. For example, an ATM center in your locality can interact with a bank server located in a different country.
Persistence
If you want to preserve the state of a particular operation in a database, just serialize it to a byte array, and save it to the database for later use.
Deep Cloning
In Java, it is also known as the deep copy. It causes an object to copy along with the objects to which it refers. You need to write a customized clone class to achieve this. Java serialization can save you the trouble of adding a clone class. Serializing the object into a byte array and then deserializing it to another object will fulfill the purpose.
Cross JVM Communication
Serialization works the same across different JVMs irrespective of the architectures they are running on.
How to Implement Serialization in Java?
Java provides an out-of-box solution to serialize an object. It outlines a set of generic interfaces. Your class needs to implement just one of them to enable serialization.
In the next section, we’ll explore these built-in interfaces and some of the ready-to-use examples. Mainly, we’ll take on the following Java serialization concepts.
Using Serializable Interface
It is the simplest way of enabling Java serialization. If you wish your class to get serialized, just implement the Java Serializable interface. It is a marker interface that doesn’t provide any method or field to implement.
What is a marker Interface in Java?
In Java, a marker interface holds a special place because it has no methods declared in it and the class implementing it needs not to override any method. A marker interface instructs the JVM to process the object suitable for a special task.
For instance, the implementation of the Serializable interface makes the JVM permit its object to write to a file.
That was a brief summary of Java Serializable Interface. Now we’ll see how to implement the Serializable interface in a Java class and apply serialization.
Java Serialization Example
We’ll use a basic example for demo purposes. For the sample, you will be creating the following class files as given below.
- SerializationDef.java
- SerializationLib.java
- SerializationDemo.java
This file defines the Java class that we’ll use for serialization. This class represents a simple Java bean carrying some properties and getter/setter methods.
By default, all the properties get serialized. But you can change this behavior. Let’s check out how to do it. However, before proceeding, you need to know the following concepts.
What is the use of the transient keyword?
The <transient> is a keyword in Java. It marks a field to exclude from serialization. You can use this keyword for a variable that you don’t want to be part of the persistent state of an object.
Would a static member of the class get serialized?
No. A static member is associated with the class, not with the object of the class. It gets memory once during the loading of the class. And gets stored in the PERMGEN
section of the heap.
We’ll now explain the above concepts using a sample project. Please follow the parts of the sample project given below.
SerializationDef.java
If you see the below class file, we’ve marked the FeatureCount
variable as transient. So it will be excluded from being serialized.
package com.techbeamers.serialization;
import java.io.Serializable;
public class SerializationDef implements Serializable {
private String Product;
private String Feature;
transient private int FeatureCount;
@Override
public String toString(){
return "Summary[Product("+Product+"),Feature("+Feature+"),FeatureCount("+FeatureCount+")]";
}
public String getProduct() {
return Product;
}
public void setProduct(String product) {
this.Product = product;
}
public String getFeature() {
return Feature;
}
public void setFeature(String feature) {
this.Feature = feature;
}
public int getFeatureCount() {
return FeatureCount;
}
public void setFeatureCount(int count) {
this.FeatureCount = count;
}
}
SerializationLib.java
For serialization, you’ll need to copy the object to a file. You may then seek to deserialize it from the same file. To achieve this, you’ll need helper functions, you can find them in the below code snippet.
You’ll notice from the code below that we are using the ObjectOutputStream
and ObjectInputStream
classes for serialization. Their methods take the Object
class variable as the argument which is the parent class of all the classes in Java.
package com.techbeamers.serialization;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SerializationLib {
// Do serialize the Java object and save it to a file
public static void doSerialize(Object obj, String outputFile)
throws IOException {
FileOutputStream fileTowrite = new FileOutputStream(outputFile);
ObjectOutputStream objTowrite = new ObjectOutputStream(fileTowrite);
objTowrite.writeObject(obj);
fileTowrite.close();
}
// Do deserialize the Java object from a given file
public static Object doDeserialize(String inputFile) throws IOException,
ClassNotFoundException {
FileInputStream fileToread = new FileInputStream(inputFile);
ObjectInputStream objToread = new ObjectInputStream(fileToread);
Object obj = objToread.readObject();
objToread.close();
return obj;
}
}
SerializationDemo.java
Until now we’ve set the basic structure of the serialization sample. Now let’s create the main file to demonstrate the serialization process.
package com.techbeamers.serialization;
import java.io.IOException;
public class SerializationDemo {
public static void main(String[] args) {
String outputFile="serializationdemo.txt";
SerializationDef def = new SerializationDef();
def.setProduct("testProduct");
def.setFeature("testFeature");
def.setFeatureCount(10);
// Serialize the object into a file.
try {
SerializationLib.doSerialize(def, outputFile);
} catch (IOException e) {
e.printStackTrace();
return;
}
// Deserialize from a file into an object.
SerializationDef defNext = null;
try {
defNext = (SerializationDef) SerializationLib.doDeserialize(outputFile);
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
}
System.out.println("def():\n --"+"\n |\n "+def);
System.out.println(System.lineSeparator());
System.out.println("defNext():\n --"+"\n |\n "+defNext);
}
}
Now we are all done. And it’s time to create a Java project in Eclipse. Add all the above files to the project. Since we’ve fully verified the code, there shouldn’t be any compile error.
If you still see any errors, please check that the JDK path is correctly set in the Eclipse preferences >> Java >> Installed JREs.
Finally, when you run the project, it’ll give the following output. From the result, you can check the value of FeatureCount
was not saved because we declared it as a transient variable.
def():
--
|
Summary[Product(testProduct),Feature(testFeature),FeatureCount(10)]
defNext():
--
|
Summary[Product(testProduct),Feature(testFeature),FeatureCount(0)]
Advanced Java Serialization with Inheritance
There are two cases when we use serialization with inheritance.
- When the parent class implements the Serializable interface, the child class does it automatically.
- If the parent class doesn’t implement the Serializable interface, then its state won’t transform into a byte stream while serializing the child class instance.
For managing the 2nd case, you need to implement the following two methods in the Child class.
readObject()
writeObject()
These methods will help in transforming the parent class state into the stream, serialize it, and finally allow retrieving it. Let’s see all of this in action.
Java Serialization Example with Inheritance
We’ll demonstrate this concept through the below sample project. The first part of the project is the file ParentClass.java
which doesn’t implement the Serializable interface.
ParentClass.java file
package com.techbeamers.serialization.inheritancedemo;
public class ParentClass {
private String Product;
private int ProductId;
public String getProduct() {
return Product;
}
public void setProduct(String product) {
this.Product = product;
}
public int getProductId() {
return ProductId;
}
public void setProductId(int Id) {
this.ProductId = Id;
}
}
ChildClass.java file
Next is the ChildClass.java
file which defines the read/write object method for preparing the stream of the parent class state. For your information, the order of reading/writing data to the byte stream would remain the same.
Another important point you’ll notice is the child class implementing the ObjectInputValidation
interface. It’ll allow overriding of some methods where you can add some business logic to ensure the data integrity.
package com.techbeamers.serialization.inheritancedemo;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectInputValidation;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class ChildClass extends ParentClass implements Serializable,
ObjectInputValidation {
private String Brand;
public String getBrand() {
return Brand;
}
public void setBrand(String brand) {
this.Brand = brand;
}
@Override
public String toString() {
return "Summary[ ProductId=" + getProductId() + ", Product=" + getProduct()
+ ", Brand=" + getBrand() + " ]";
}
// adding helper method for serialization to save/initialize parent class
// state
private void readObject(ObjectInputStream ois)
throws ClassNotFoundException, IOException {
ois.defaultReadObject();
// notice the order of read and write should be same
setProductId(ois.readInt());
setProduct((String) ois.readObject());
}
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
oos.writeInt(getProductId());
oos.writeObject(getProduct());
}
@Override
public void validateObject() throws InvalidObjectException {
// validate the object here
if (Brand == null || "".equals(Brand))
throw new InvalidObjectException("Brand is not set or empty.");
if (getProductId() <= 0)
throw new InvalidObjectException("ProductId is not set or zero.");
}
}
InheritanceDemo.java file
Let’s now create a demo class file to serialize/deserialize the child class data including the parent. And see whether we can retrieve the parent class state from the serialized form.
package com.techbeamers.serialization.inheritancedemo;
import java.io.IOException;
import com.techbeamers.serialization.SerializationLib;
public class InheritanceDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
String fileName = "childclass.txt";
ChildClass childClass = new ChildClass();
childClass.setProductId(21);
childClass.setProduct("Blog");
childClass.setBrand("TechBeamers");
try {
SerializationLib.doSerialize(childClass, fileName);
} catch (IOException e) {
e.printStackTrace();
return;
}
try {
ChildClass newChild = (ChildClass) SerializationLib
.doDeserialize(fileName);
System.out.println("ChildClass output: \n |\n --" + newChild);
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
}
}
}
When you execute the above code, you’ll get the following output.
ChildClass output:
|
--Summary[ ProductId=21, Product=Blog, Brand=TechBeamers ]
Class Refactoring with Java Serialization
Java serialization permits the refactoring of the underlying class. The following is the list of changes that are allowed in a class and won’t disturb the deserialization process.
- You can add new members to the class.
- Switching of a variable from transient to non-transient is allowed. However, the serialization will consider such variables as new.
- Make a variable from static to non-static. Serialization will count this as a new variable.
However, Java imposes a condition for all these changes to work. You can fulfill it by adding a unique identifier, serialVersionUID
in the class to track the modifications under a common tag. By default, serialization will automatically compute serialVersionUID
by going through all the fields and methods. That’s why if you try to alter any class variable without manually specifying the version identifier, the JVM will throw the java.io.InvalidClassException
as it detects a change in the identifier value.
How to generate a <serialVersionUID>?
There are three ways you can use to produce a <serialVersionUID> value.
Using the <serialver> command
JDK bundles a tiny command line utility called the <serialver>. You just need to pass the serializable class name as a command parameter to get its version identifier.
C:\Working\SerializationDemo>serialver com.techbeamers.serialization.SerializationDef
SerializationDemo: static final long serialVersionUID = -2456709228636810878L;
Before you run the above command, make sure you have set the path to the JDK’s bin folder which contains the above command line tool.
Using Eclipse IDE
In the Eclipse IDE, hover over the serialization class. It’ll open a context menu and display three options. Choose any of the first two options as given below.
- Add default serial version ID, or
- Add generated serial version ID.
Assign the value of your choice
Just pick a number you like and set it as a serial version ID. But do postfix the number with an “L”.
private static final long serialVersionUID = 21L;
Using Java Externalizable Interface for Serialization
The default serialization method is not secure and has performance issues. You can see the list below to check the performance issues with default serialization.
- Serialization depends on the recursion mechanism. When serialization of a child class object starts, it triggers the serialization of other instance variables from the chain of parent classes which continues until it reaches the Object class of these variables. It leads to a lot of overheads.
- While serializing an object class description information is attached to the stream. Lots of data and metadata stretch down the performance.
- Serialization also needs a serial version ID for tracking class-level changes. If you don’t set it manually, serialization calculates it automatically by going through all the fields and methods. The more the size of the class more will be the time to calculate the value. So this is again a potential performance issue.
- We can solve all of the above issues with the Externalization interface.
What is the Externalizable interface and how is it better than the Serializable interface?
Externalization is an extension of the Serializable interface. If you want to externalize an object, then your class needs to implement the Externalizable interface and a default public constructor.
In the Externalization process, only the identity of the class is added to the serialization stream. And, the class is responsible for the storage and retrieval of the instance information. It gives complete control of what to add and what to leave during serialization. The basic serialization doesn’t offer similar flexibility.
For your information, Externalizable is not a marker interface instead it exposes two methods – writeExternal and readExternal. The target class implements these methods to seize full control over the format and contents of the stream relating to the object and its supertypes.
These methods must have the ability to coordinate with the object’s supertypes to save its state. They supersede the tailor-made implementation of writeObject
and readObject
methods.
Now it’s time to create an Eclipse sample project to demonstrate the use of the Externalizable interface. In this sample project, we’ll add the following two Java class files.
- UIMap.java, this file defines a class that implements the Externalizable interface and provides the
readExternal()
andwriteExternal()
methods. - UIMapDemo.java, this file will create the UIMap object and test the Java serialization process via the Externalizable interface.
Java Externalizable Example
We are embedding the source code of these files so that you can instantly add it to your local Java project. The first code snippet relates to the UIMap.java
class file, have a look at the below code.
UIMap.java class file
package com.techbeamers.externalization;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
public class UIMap implements Externalizable {
private int id;
private String locator;
private String value;
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(id);
out.writeObject(locator + "$$");
out.writeObject("##" + value);
}
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
id = in.readInt();
// Retrieve data in the same sequence as written
locator = (String) in.readObject();
if (!locator.endsWith("$$"))
throw new IOException("data integrity failed.");
locator = locator.substring(0, locator.length() - 2);
value = (String) in.readObject();
if (!value.startsWith("##"))
throw new IOException("data integrity failed.");
value = value.substring(2);
}
@Override
public String toString() {
return "UIMap[ id=" + id + ",locator=" + locator + ",value=" + value + " ]";
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getLocator() {
return locator;
}
public void setLocator(String locator) {
this.locator = locator;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
UIMapDemo.java class file
Now, see the UIMapDemo.java
file to test the functionality of the Java serialization using the Externalizable interface.
package com.techbeamers.externalization;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
public class UIMap implements Externalizable {
private int id;
private String locator;
private String value;
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(id);
out.writeObject(locator + "$$");
out.writeObject("##" + value);
}
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
id = in.readInt();
// Retrieve data in the same sequence as written
locator = (String) in.readObject();
if (!locator.endsWith("$$"))
throw new IOException("data integrity failed.");
locator = locator.substring(0, locator.length() - 2);
value = (String) in.readObject();
if (!value.startsWith("##"))
throw new IOException("data integrity failed.");
value = value.substring(2);
}
@Override
public String toString() {
return "UIMap[ id=" + id + ",locator=" + locator + ",value=" + value + " ]";
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getLocator() {
return locator;
}
public void setLocator(String locator) {
this.locator = locator;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
When we execute the above sample Java project, it’ll generate the following output.
UIMap Object Summary:
|
-- UIMap[ id=2,locator=cssSelector,value=input[id=email] ]
Download Sample Project
Now it’s time to download the sample project so that you can easily play around with the Java serialization examples. Please use the below link to start your download.
We’ve recently published an interesting quiz on the Java serialization concept. And we highly recommend you to attempt this questionnaire. It’ll test your knowledge level on this subject.
Check This Before Leaving
We hope that this tutorial illustrating serialization in different ways will have helped you move a step further in learning Java. We wanted to cover the whole Java serialization topic and the little things around it in a single article.
With this tutorial, we gave you the most appropriate detail on Serialization in Java. However, you can get more micro-level information on this topic from Oracle’s website.
Lastly, our site needs your support to remain free. Share this post on social media (Linkedin/Twitter) if you gained some knowledge from this tutorial.
All the Best,
TechBeamers.