Course – LSD (cat=Persistence)

Get started with Spring Data JPA through the reference Learn Spring Data JPA course:

>> CHECK OUT THE COURSE
Course – LS (cat=Java)

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

> CHECK OUT THE COURSE

1. Overview

In this quick tutorial, we'll learn how to create a serializable singleton class in Java.

2. What Is Serialization?

Serialization is the process of converting the state of a java object to a byte stream that can be stored in a file or database:

Serialization

Deserialization is the opposite. It creates the object from the byte stream:

Deserialization

3. The Serializable Interface

The Serializable interface is a marker interface (also called tagging interface). A marker interface provides run-time type information about objects for the compiler and the JVM. It doesn't have any fields, methods, or constants inside it. Therefore the classes that implement it don't have to implement any methods.

If a class implements the Serializable interface, its instances can be serialized or deserialized.

4. What Is Singleton Class?

In object-oriented programming, a singleton class is a class that can have only one instance at a time. After the first instantiation, if we try to instantiate the singleton class again, it gives us the same instance it created the first time. Here is a singleton class that implements the Serializable interface:

public class Singleton implements Serializable {

    private static Singleton INSTANCE;
    private String state = "State Zero";

    private Singleton() {}
    
    public static Singleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new Singleton();
        }
        
        return INSTANCE;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }
}

We can see that it has two private fields: INSTANCE and state. INSTANCE is the only instance of the singleton class, and state is a string variable that holds the state of the class.

5. Creating a Serializable Singleton Class

The problem is, after instantiating a singleton class that implements Serializable, and then serializing and deserializing the instance, we'll end up with two instances of the singleton class, which violates singleton-ness:

@Test
public void givenSingleton_whenSerializedAndDeserialized_thenStatePreserved() {
    Singleton s1 = Singleton.getInstance();
    s1.setState("State One");

    try (
      FileOutputStream fos = new FileOutputStream("singleton_test.txt");
      ObjectOutputStream oos = new ObjectOutputStream(fos);
      FileInputStream fis = new FileInputStream("singleton_test.txt");
      ObjectInputStream ois = new ObjectInputStream(fis)) {

        // Serializing.
        oos.writeObject(s1);

        // Deserializing.
        Singleton s2 = (Singleton) ois.readObject();

        // Checking if the state is preserved.
        assertEquals(s1.getState(), s2.getState());

        // Checking if s1 and s2 are not the same instance.
        assertNotEquals(s1, s2);

    } catch (Exception e) {
        // ...
    }
}

The above test code passes. So, even though the state is preserved during serialization and deserialization, the new variable, s2, doesn't point to the same instance as s1. Therefore, there are two instances of the Singleton class, which is not good.

To create a serializable singleton class, we should use the enum singleton pattern:

public enum EnumSingleton {

    INSTANCE("State Zero");

    private String state;

    private EnumSingleton(String state) {
        this.state = state;
    }

    public EnumSingleton getInstance() {
        return INSTANCE;
    }

    public String getState() { 
        return this.state; 
    }

    public void setState(String state) { 
        this.state = state; 
    }
}

Now let's see what happens if we serialize and deserialize it:

@Test
public void givenEnumSingleton_whenSerializedAndDeserialized_thenStatePreserved() {
    EnumSingleton es1 = EnumSingleton.getInstance();
    es1.setState("State One");

    try (
      FileOutputStream fos = new FileOutputStream("enum_singleton_test.txt");
      ObjectOutputStream oos = new ObjectOutputStream(fos);
      FileInputStream fis = new FileInputStream("enum_singleton_test.txt");
      ObjectInputStream ois = new ObjectInputStream(fis)) {
        
        // Serializing.
        oos.writeObject(es1);

        // Deserializing.
        EnumSingleton es2 = (EnumSingleton) ois.readObject();

        // Checking if the state is preserved.
        assertEquals(es1.getState(), es2.getState());

        // Checking if es1 and es2 are pointing to 
        // the same instance in memory.
        assertEquals(es1, es2);

    } catch (Exception e) {
        // ...
    }
}

The above test code passes. So, the state is preserved after serialization and deserialization, and the two variables es1 and es2 point to the same instance that was created initially.

6. Summary

In this tutorial, we learned how to create a serializable singleton class in Java.

As always, the complete code samples are available over on GitHub.

Course – LSD (cat=Persistence)

Get started with Spring Data JPA through the reference Learn Spring Data JPA course:

>> CHECK OUT THE COURSE
Course – LS (cat=Java)

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE
res – Persistence (eBook) (cat=Persistence)
Comments are closed on this article!