In this blog, you will explore serialization, consisting of examples, how serialization can be used in inheritance, aggregation, and static data members, along with its methods and benefits in Java.
Table of Content
Watch this Java video by Intellipaat:
What is Serialization in Java?
Serialization in Java is the mechanism through which objects are transformed into a portable format that can be easily stored, transmitted, or reconstructed. This process involves converting the state of an object—its data and structure—into a sequence of bytes.
At its core, Java’s serialization allows objects to transcend the limitations of a single program or system. Objects are converted into a stream of bytes using the java.io.Serializable interface. This interface serves as a marker, indicating that a class’s instances can be serialized. By implementing this interface, classes enable Java to serialize their objects, transforming them into a format that can travel across networks, persist in storage, or be temporarily held in memory.
The serialized byte stream retains the essential characteristics and state of the original object. Once serialized, the object can be transmitted over networks or stored in files, databases, or other forms of persistent storage. Later, these byte streams can be deserialized—converted back into objects—recreating the original object’s state, structure, and behavior.
Serialization in Java with Inheritance (Is-A Relationship)
Serialization with inheritance in Java allows subclasses to inherit the serialization behavior of their parent classes. Here’s an example demonstrating how serialization works with an inheritance in Java:
import java.io.*;
// Parent class
class Parent implements Serializable {
private static final long serialVersionUID = 1L;
private String parentName;
public Parent(String parentName) {
this.parentName = parentName;
}
public String getParentName() {
return parentName;
}
}
// Subclass inheriting from Parent
class Child extends Parent {
private static final long serialVersionUID = 2L;
private String childName;
public Child(String parentName, String childName) {
super(parentName);
this.childName = childName;
}
public String getChildName() {
return childName;
}
}
public class SerializationWithInheritance {
public static void main(String[] args) {
Child child = new Child("Intelli", "Paat");
// Serialization
try {
FileOutputStream fileOut = new FileOutputStream("child.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(child);
out.close();
fileOut.close();
System.out.println("Child object has been serialized");
} catch (IOException e) {
e.printStackTrace();
}
// Deserialization
try {
FileInputStream fileIn = new FileInputStream("child.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
Child deserializedChild = (Child) in.readObject();
in.close();
fileIn.close();
System.out.println("Child object has been deserialized");
System.out.println("Parent Name: " + deserializedChild.getParentName());
System.out.println("Child Name: " + deserializedChild.getChildName());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Output:
Child object has been serialized
Child object has been deserialized
Parent Name: Intelli
Child Name: Paat
In this example, Parent is the parent class implementing Serializable, and Child is the subclass inheriting from Parent. Both classes have their own serialVersionUID to control serialization compatibility.
The Child class inherits serialization behavior from Parent, allowing both Parent and Child objects to be serialized and deserialized seamlessly. When deserialized, the child object retains the state of its parent and child attributes.
Get 100% Hike!
Master Most in Demand Skills Now!
Serialization in Java with Aggregation (Has-A Relationship)
Serialization with aggregation (Has-A relationship) in Java involves serializing objects that are contained within another object. Here’s an example demonstrating serialization with aggregation:
import java.io.*;
// Class representing an Address
class Address implements Serializable {
private static final long serialVersionUID = 1L;
private String street;
private String city;
public Address(String street, String city) {
this.street = street;
this.city = city;
}
public String getStreet() {
return street;
}
public String getCity() {
return city;
}
}
// Class containing Address object
class Person implements Serializable {
private static final long serialVersionUID = 2L;
private String name;
private transient Address address; // transient - won't be serialized
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public Address getAddress() {
return address;
}
}
public class SerializationWithAggregation {
public static void main(String[] args) {
Address address = new Address("123 Main St", "Cityville");
Person person = new Person("Intellipaat", address);
// Serialization
try {
FileOutputStream fileOut = new FileOutputStream("person.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(person);
out.close();
fileOut.close();
System.out.println("Person object has been serialized");
} catch (IOException e) {
e.printStackTrace();
}
// Deserialization
try {
FileInputStream fileIn = new FileInputStream("person.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
Person deserializedPerson = (Person) in.readObject();
in.close();
fileIn.close();
System.out.println("Person object has been deserialized");
System.out.println("Name: " + deserializedPerson.getName());
System.out.println("Address: " + deserializedPerson.getAddress().getStreet()
+ ", " + deserializedPerson.getAddress().getCity());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Output:
Person object has been serialized
Person object has been deserialized
Name: Intellipaat
Address: null, null
In this example, the Address class represents an address with street and city attributes. The Person class contains an Address object, establishing a Has-A relationship. The Person class implements Serializable, while the Address class also implements Serializable.
The Person object and its associated Address object are serialized together. The transient keyword is used for the address field in the Person class to indicate that it should not be serialized. Therefore, when deserialized, the Person object will have a null for the address field initially. This approach can be used to exclude specific fields from serialization.
Serialization in Java with Static Data Member
Serializing static members in Java can be a bit tricky due to their nature. Static members belong to the class rather than to individual objects, and they aren’t part of the object’s state. Therefore, they are not serialized along with the object’s instance variables by default.
Here’s an example to illustrate serialization with a static data member in Java:
import java.io.*;
class MyClass implements Serializable {
private static final long serialVersionUID = 1L;
private static String staticField = "Hello, I am a static field!";
private transient String nonStaticField;
public MyClass(String nonStaticField) {
this.nonStaticField = nonStaticField;
}
public static String getStaticField() {
return staticField;
}
public String getNonStaticField() {
return nonStaticField;
}
}
public class SerializationWithStaticDataMember {
public static void main(String[] args) {
MyClass object = new MyClass("This is a non-static field.");
// Serialize the object
try {
FileOutputStream fileOut = new FileOutputStream("object.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(object);
out.close();
fileOut.close();
System.out.println("Object serialized successfully in Intellipaat Compiler.");
} catch (IOException e) {
e.printStackTrace();
}
// Deserialize the object
try {
FileInputStream fileIn = new FileInputStream("object.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
MyClass deserializedObject = (MyClass) in.readObject();
in.close();
fileIn.close();
// Accessing static and non-static fields after deserialization
System.out.println("Static Field after Deserialization: " + MyClass.getStaticField());
System.out.println("Non-Static Field after Deserialization: " + deserializedObject.getNonStaticField());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Output:
Object serialized successfully in Intellipaat Compiler.
Static Field after Deserialization: Hello, I am a static field!
Non-Static Field after Deserialization: null
In this example, MyClass implements Serializable. It contains a staticField (a static member) and a nonStaticField (a non-static member). However, during serialization, static fields are not serialized as they do not belong to any particular instance of the class.
When the object is deserialized, the static field retains its original value as it is associated with the class itself, not with individual objects. The deserialized object will contain the non-static fields with their updated values, but the static field remains unchanged. This demonstrates how static members behave during serialization and deserialization in Java.
Java Serialization Methods
In Java, serialization involves converting objects into byte streams and reconstructing them back into objects. Key methods and classes involved in serialization include:
- java.io.Serializable Interface: This interface marks classes as serializable, allowing their instances to be serialized.
- ObjectOutputStream: This class converts objects into a byte stream. Key methods include:
- writeObject(Object obj): Serializes an object into a byte stream.
- flush(): Flushes the stream, ensuring any buffered data is written.
- ObjectInputStream: This class reads objects from a byte stream. Key methods include:
- readObject(): Deserializes an object from the byte stream.
- close(): Closes the stream and releases system resources.
- writeObject() and readObject() methods: These are user-defined methods in a Serializable class allowing customized serialization and deserialization logic.
- Externalizable Interface: An alternative to Serializable, Externalizable allows classes to customize the serialization process via readExternal() and writeExternal() methods.
- serialVersionUID: A static final long variable used to ensure versioning compatibility during deserialization. It’s manually declared within a serializable class to control versioning.
Difference Between Serialization and Deserialization in Java
Here’s a tabular representation outlining the differences between Serialization and Deserialization in Java:
Aspect | Serialization | Deserialization |
Definition | Converts objects into byte streams | Reconstructs objects from byte streams |
Objective | Enables storage or transmission of object state | Restores objects to their original state |
Process | Converts object state into byte sequence | Reads a byte stream to reconstruct objects |
Mechanism | Uses ObjectOutputStream to write bytes | Uses ObjectInputStream to read bytes |
Interface | Implemented by Serializable classes | Performed on Serializable/Externalizable |
Direction | Object to byte stream | Byte stream to object |
Main Goal | Portability and persistence of object state | Restoration of object state and structure |
Advantages of Java Serialization
Here are the following advantages of Java Serialization:
- Framework Integration: Serialization is integrated into various Java frameworks and technologies. For instance, it’s used in Java RMI (Remote Method Invocation) for remote communication between Java applications, enabling objects to be passed between client and server.
- Caching and Performance: Serialized objects can be cached, improving performance by reducing the need to recreate objects repeatedly. Once an object is deserialized, it can be stored in memory and reused when needed, reducing computational overhead.
- Flexibility in Versioning: Java provides mechanisms to handle class versioning and backward compatibility during deserialization. It allows older versions of classes to deserialize objects serialized by newer versions of the same classes, handling changes in class structures or fields gracefully.
- Security: Serialization can be used for securing sensitive information by encrypting the serialized data or by applying custom encryption mechanisms to the stream of bytes.
Conclusion
While serialization remains a crucial aspect of Java programming, its future lies in adapting to evolving technological needs and challenges. Enhancements focused on performance, security, compatibility, and interoperability are expected to shape the future landscape of serialization in Java, ensuring it remains a relevant and efficient tool for developers in the years to come.