Java Serialization and Deserialization

JavaJavaBeginner
Practice Now

Introduction

Serialization and Deserialization are important processes in Java that allow us to convert an object into a byte stream and restore it back into an object respectively. We can use serialization and deserialization to save or persist the state of objects, or to transmit objects over a network. In this lab, we will learn how to serialize and deserialize objects in Java using the ObjectInputStream and ObjectOutputStream classes.

Implement the Serializable Interface

In order to make our objects serializable, we need to implement the Serializable interface. This is a marker interface that doesn't require any methods to be implemented. The Serializable interface is used to indicate to the JVM that the objects of this class can be serialized.

import java.io.Serializable;

public class Student implements Serializable {
    private String name;
    private int age;

    // Constructor and getter/setter methods
}

Create Objects to Serialize

Create some objects of the Student class that we will serialize in the next steps.

Student student1 = new Student("Alice", 22);
Student student2 = new Student("Bob", 21);

Serialize Objects

To serialize an object, we need to create an instance of ObjectOutputStream and call its writeObject() method passing in the object we want to serialize. This will write the object as a byte stream to a file that we can later use for deserialization.

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;

public class SerializationDemo {
    public static void main(String[] args) {
        try {
            FileOutputStream fileOut = new FileOutputStream("students.ser");
            ObjectOutputStream out = new ObjectOutputStream(fileOut);

            out.writeObject(student1);
            out.writeObject(student2);

            out.close();
            fileOut.close();

            System.out.println("Serialized data is saved in students.ser file");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Deserialize Objects

To deserialize an object, we need to create an instance of ObjectInputStream and call its readObject() method to read the byte stream from the file we serialized earlier. This will return an Object which we can then cast to our original class type.

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.IOException;

public class SerializationDemo {
    public static void main(String[] args) {
        try {
            FileInputStream fileIn = new FileInputStream("students.ser");
            ObjectInputStream in = new ObjectInputStream(fileIn);

            Student student1 = (Student) in.readObject();
            Student student2 = (Student) in.readObject();

            in.close();
            fileIn.close();

            // Print the students
            System.out.println(student1);
            System.out.println(student2);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

Handle Transient Fields

If we have any fields in our class that we don't want to be serialized, we can declare them as transient. Transient fields are ignored during serialization and deserialization. For example, let's add a transient field to our Student class that we don't want to be serialized:

import java.io.Serializable;

public class Student implements Serializable {
    private String name;
    private int age;
    private transient String password;

    //Constructor, getter/setter methods
}

Customize Serialization and Deserialization

We can also customize the serialization and deserialization process by implementing the writeObject() and readObject() methods. This can be useful if we want to handle transient fields, or if we want to perform additional processing during serialization and deserialization.

private void writeObject(ObjectOutputStream oos) throws IOException {
    // Default serialization
    oos.defaultWriteObject();

    // Encrypt password before writing to file
    String encryptedPassword = encrypt(password);
    oos.writeObject(encryptedPassword);
}

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
    // Default deserialization
    ois.defaultReadObject();

    // Decrypt password after reading from file
    String encryptedPassword = (String) ois.readObject();
    password = decrypt(encryptedPassword);
}

Handle Versioning

If we update our class by adding or removing properties, then the version of the class changes, which can cause errors when deserializing older objects. We can handle versioning by using the serialVersionUID field to specify a unique identifier for our class. This will ensure that serialization and deserialization will work correctly even if the class definition changes.

import java.io.Serializable;

public class Student implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name;
    private int age;

    //Constructor, getter/setter methods
}

Run the Code

Compile the SerializationDemo class and run it in the terminal using the following command:

$ javac SerializationDemo.java
$ java SerializationDemo

The output should show the serialized data and the deserialized objects.

Modify the Student Class

Update the Student class by adding or removing a field:

public class Student implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name;
    private int age;
    private String email; // Added a field

    //Constructor, getter/setter methods
}

Handle Versioning with SerialVersionUID

Now that we've modified our class, we need to handle versioning by updating the serialVersionUID. This will ensure that we can still deserialize objects that were serialized before the class was modified.

public class Student implements Serializable {
    private static final long serialVersionUID = 2L;

    private String name;
    private int age;
    private String email; // Added a field

    //Constructor, getter/setter methods
}

Summary

In this lab, we learned how to serialize and deserialize objects in Java using the ObjectInputStream and ObjectOutputStream. We also learned how to handle transient fields, customize the serialization and deserialization process, and handle versioning using serialVersionUID. Serialization and deserialization are important processes for saving and transmitting data in Java, and we can use them to store the current state of our objects and later restore them as necessary.

Other Java Tutorials you may like