What You Need to Know About Serialization
Get a good look at serialization, deserialization, and securing your serialized objects with this example code.
Join the DZone community and get the full member experience.
Join For FreeAre these questions puzzling you when you try to dig deeper into serialization?
What happens when final transient field gets serialized?
What happens when static transient or final static transient field gets serialized?
What happens to private members of a serializing class?
What if a super class or interface does not implement Serializable while the derived class is getting serialized?
How do I validate an object after serialization? Is it really important?
How to get auto generated or private serialversionuid while deserialization in progress?
What happens to non-serialized aggregated object i.e. object with "has-a" relationship?
Well, all these questions are going to be answered with a code example, but before that, let's get a feel for serialization.
In plain English, serialization is a process 'to arrange in a series and broadcast it to the outer world'. So in Java, we send a serializing object to the network stream and publish or send it to a directory to store its form for the future use.
Let's jump on an example to learn more. And if you want to know even more, be sure to check out the 'points to be noted' section.
Employee.java
import java.io.InvalidObjectException;
import java.io.ObjectInputValidation;
import java.io.Serializable;
public class Employee extends Manager implements Company, Serializable, ObjectInputValidation {
private static final long serialVersionUID = 456778567857L;
public String firstName = "First Name";
public transient static String middleName = "Middle Name";
public transient final String lastName;
public transient final String nickName = "Nick Name";
private int explicitAge = 45;//Will not be read
PersonalDetails pd = new PersonalDetails(26,'F');//If we make it transient then null pointer exception
transient static PersonalDetails pdstat = new PersonalDetails(30,'M');
public Employee(String lastName) {
this.lastName = lastName;
}
@Override
public void validateObject() throws InvalidObjectException {
// TODO Auto-generated method stub
System.err.println("Object Validation In Progress");
if(this.explicitAge == 45 || this.nickName.equals("Nick Name")) {
System.err.println("Object Validation Passed");
}else{
throw new InvalidObjectException("Object Validation Failed");
}
}
}
class Manager {
public transient String managerName = "Manager Name";//Transient field wont work here as Manager is not implementing Serializable
}
interface Company {
String companyCEO = "Mr CEO";
String companyName = "Company Name";
}
class PersonalDetails implements Serializable {
transient int age = 40;
char gender = 'F';
public PersonalDetails(int age, char gender) {
this.age = age;
this.gender = gender;
}
}
Points to be Noted in Employee.java:
serialVersionUID is explicitly defined to prevent InvalidClassException during deserialization, as the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations and can produce different serialVersionUIDs in different environments.
firstName will be serialized, and the default value can be overridden in subsequent classes that are serializing Employee.java
middleName, lastName and nickName are either transient static or transient final. Always remember, transient and static fields are never get serialized. The value which will be restored for these variables will be the value mentioned in the Employee.class at the time of deserialization.
explicitAge is private. It will be serialized as well but can be read only through Reflection API.
PersonalDetails is a class that is implementing a serializable interface. If it won't, then NullPointerException will be thrown at the time of serialization. Its field's value can be overridden in the subsequent classes. Note that the 'age' field is transient.
There is an interface available to validate the object and its data called 'ObjectInputValidation'. And to validate, the validateObject() method needs to be overridden.
Please note that the parent class 'Manager' is not implementing the Serializable interface. Though it won't throw any exception, as we are not composing or aggregating the parent class object inside the child class Employee.java. So here, the only point we can observe is that the transient keyword won't work here.
'Company' is an interface, so all its fields are final by default. Please note Interface does not accept the transient keyword for its member variables.
SerializaitonClass.java
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class SerializaitonClass {
public static void main(String[] args) {
Employee emp = new Employee("Last Name");
emp.firstName = "Overriden First Name";
emp.middleName = "Overriden Middle Name";
emp.managerName = "Overriden Manager Name";
emp.pd.age = 28;
emp.pd.gender = 'M';
emp.pdstat.age = 35;
emp.pdstat.gender = 'M';
try {
FileOutputStream fileOut = new FileOutputStream("./employee.txt");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(emp);
out.close();
fileOut.close();
System.out.printf("Serialized data is saved in ./employee.txt file");
} catch (IOException i) {
i.printStackTrace();
}
}
}
DeserializationClass.java
import java.io.*;
public class DeserializationClass{
public static void main(String[] args) {
Employee emp = null;
try {
FileInputStream fileIn = new FileInputStream("./employee.txt");
ObjectInputStream in = new ObjectInputStream(fileIn);
emp = (Employee) in.readObject();
emp.validateObject();
in.close();
fileIn.close();
} catch (IOException i) {
i.printStackTrace();
return;
} catch (ClassNotFoundException c) {
System.out.println("Employee class not found");
c.printStackTrace();
return;
}
System.out.println("serialVersionUID = "+ObjectStreamClass.lookup(emp.getClass()).getSerialVersionUID());
System.out.println("Deserializing Employee...");
System.out.println("First Name of Employee: " + emp.firstName);
System.out.println("Middle Name of Employee: " + emp.middleName);
System.out.println("Last Name of Employee: " + emp.lastName);
System.out.println("Nick Name of Employee: " + emp.nickName);
System.out.println("Age of Employee: " + emp.pd.age);
System.out.println("Gender of Employee: " + emp.pd.gender);
System.out.println("Static Age of Employee: " + emp.pdstat.age);
System.out.println("Static Gender of Employee: " + emp.pdstat.gender);
System.out.println("Company Name: "+emp.companyName);
System.out.println("Company CEO: "+emp.companyCEO);
System.out.println("Manager Name: "+emp.managerName);
}
}
Output
Still thinking about the security of your object? If you want to encrypt and sign the entire object, then go for either the javax.crypto.SealedObject and/or java.security.SignedObject wrappers. Both are serializable and box the original object.
But what happens to the Singleton class when it gets serialized? Oops, it creates the new object! Don't worry! Implement the following method, which can be used to unpack the proxy.
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
Still have questions? Help me to help yourselves.
Opinions expressed by DZone contributors are their own.
Trending
-
Effortlessly Streamlining Test-Driven Development and CI Testing for Kafka Developers
-
What Is Test Pyramid: Getting Started With Test Automation Pyramid
-
A Complete Guide to AWS File Handling and How It Is Revolutionizing Cloud Storage
-
Apache Kafka vs. Message Queue: Trade-Offs, Integration, Migration
Comments