Contents

Java Pattern: Singleton

Writen by: David Vlijmincx

Introduction

This post will look at many ways to implement the Singleton pattern in Java. We will focus on the intent of the Singleton pattern, implementing it, and benefits of choosing one implementation over the other.

What is a Singleton pattern in Java?

This pattern ensures that there is only one instance of a class inside your application. When developing your application, it can be essential that only one instance of a class is available at a time. A use-case of the singleton pattern could be: many clients need access to the same logging instance when you create a logging framework. Another use-case could be an instance of a file-system instance that you want to share with multiple clients.

The Singleton pattern works by giving the class the responsibility to create and manage its instance. We achieve this by making the constructor private so other classes can't call the constructor to create an instance, instead, we provide one (or more) methods for clients to retrieve a reference to the singleton instance

Why Singleton pattern is used in Java?

The Singleton pattern gives controlled access to a class, its only instance, and its creation. This encapsulation allows us to control how and if a client could obtain a reference to the instance. The Singleton pattern could also be altered to support multiple instances instead of a single one. To do this, you would only have to change or add a method the clients use to obtain a reference to the instance.

Using Class for Singleton pattern

We will start by implementing the Singleton pattern using a Java class. In the example below, we have the class SingletonClass and made its constructor private and implemented a static method for clients to obtain a reference to the instance.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class SingletonClass {

    private int amount;

    private static SingletonClass instance = null;

    private SingletonClass(){}

    public static SingletonClass getInstance(){
        if (instance == null) {
            instance = new SingletonClass();
        }
        return instance;
    }
    
    // getters and setters for amount
}

Here is an example of how we could obtain and use a reference to the singleton class instance. In the example, we obtain two references and use one to set the amount and the other reference to get the amount. The output to the console for this code is anotherReference amount = 1.

1
2
3
4
5
SingletonClass firstReference = SingletonClass.getInstance();
SingletonClass anotherReference = SingletonClass.getInstance();

firstReference.setAmount(1);
System.out.println("anotherReference amount = " + anotherReference.getAmount());

Eager initialization of the Singleton

When we eagerly initialize a singleton class, we make sure that the instance of the class is created before a client calls the method to obtain a reference to the instance.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class SingletonClass {

    private static final SingletonClass instance = new SingletonClass();

    private SingletonClass(){}

    public static SingletonClass getInstance(){
        return instance;
    }

}

Lazy initialization of the Singleton

Creating the singleton instance when a client asks for a reference is called lazy initialization. Implementing the pattern this way requires you to add some logic to check if the instance is already created. The first call made to get an instance wilL also take a longer time to complete because the instance has to be initialized for the first time.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class SingletonClass {
    
    private static SingletonClass instance = null;

    private SingletonClass(){}

    public static SingletonClass getInstance(){
        if (instance == null) {
            instance = new SingletonClass();
        }
        return instance;
    }
    
}

Can we serialize singleton class in Java?

You can use serialization to create multiple instances of a Singleton. To prevent others from creating numerous instances, your class must implement the Serializable interface and the readResolve() method. The readResolve method should return the singleton instance that has already been created and is referenced by other clients. Make sure also to make the fields transient, so they can't be misused during serialization.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class SerializableSingletonClass implements Serializable {

    private transient int amount;

    private static SerializableSingletonClass instance = null;

    private SerializableSingletonClass(){}

    public static SerializableSingletonClass getInstance(){
        if (instance == null) {
            instance = new SerializableSingletonClass();
        }
        return instance;
    }

    protected Object readResolve() {
        return getInstance();
    }

    // getters and setters
}

Support a variable amount of Singleton instances

We can implement the Singleton in a way that supports a variable amount of singletons. We can do this because the responsibility for creating instances is encapsulated inside the class. The example below uses a map to keep track of all the created instances. In the example below, an instance of VariableAmountOfSingletonClass is associated with only one name; there can't be two instances with the same name.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class VariableAmountOfSingletonClass {

    private int amount;

    private String name;

    private static Map<String, VariableAmountOfSingletonClass> instances = new HashMap<>();

    private VariableAmountOfSingletonClass() {
    }

    public static VariableAmountOfSingletonClass getInstance(String name) {
        VariableAmountOfSingletonClass variableAmountOfSingletonClass = instances.get(name);

        if (variableAmountOfSingletonClass == null) {
            variableAmountOfSingletonClass = new VariableAmountOfSingletonClass();
            variableAmountOfSingletonClass.setName(name);
            instances.put(name, variableAmountOfSingletonClass);
        }
        return variableAmountOfSingletonClass;
    }

    // getter and setter for amount
    
    public String getName() {
        return name;
    }

    private void setName(String name) {
        this.name = name;
    }
}

Here is an example of how we would use this class:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
VariableAmountOfSingletonClass first = VariableAmountOfSingletonClass.getInstance("first");
VariableAmountOfSingletonClass sameAsFirst = VariableAmountOfSingletonClass.getInstance("first");
sameAsFirst.setAmount(1);

VariableAmountOfSingletonClass second = VariableAmountOfSingletonClass.getInstance("second");
second.setAmount(2);

System.out.println("first amount = " + first.getAmount());
System.out.println("sameAsFirst amount = " + sameAsFirst.getAmount());
System.out.println("second amount = " + second.getAmount());

The console output for this code is;

1
2
3
first amount = 1
sameAsFirst amount = 1
second amount = 2

Using Enum for Singleton pattern

Using an Enum is considered the best way to implement the Singleton pattern in Java. It ensures that there will be only one instance of the singleton inside our application. When you use Enum you don't have to worry about reflection, and you don't need to implement the Serializable interface yourself.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public enum SingletonEnum {
    INSTANCE;

    private int amount;

    public int getAmount() {
        return amount;
    }

    public void setAmount(int amount) {
        this.amount = amount;
    }
}

How you could use this Singleton:

1
2
3
4
5
6
SingletonEnum singletonEnum = SingletonEnum.INSTANCE;
singletonEnum.setAmount(42);

SingletonEnum singletonEnumSecondReference = SingletonEnum.INSTANCE;

System.out.println("singletonEnumSecondReference amount = " + singletonEnumSecondReference.getAmount());

Conclusion

We implemented the Singleton pattern in Java in this post using a class and enum; and the benefits of using an Enum to implement this pattern. We also saw the difference between lazy and eager initialization of the singleton instance. Using a Map, we also implemented a variation of the Singleton pattern that supports multiple instances because the initialization is encapsulated inside its class.