Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Prevent Breaking a Singleton Class Pattern

DZone 's Guide to

Prevent Breaking a Singleton Class Pattern

Let's explore the three major ways in which you can break a singleton property — and how to prevent it.

· Java Zone ·
Free Resource

Image title

Learn how to avoid breaking a Singleton class pattern

We are used to having a singleton design pattern in our applications whenever it is needed. As we know, in the singleton design pattern, we can only create one instance and access it in the whole application. But in some cases, it will break the singleton behavior.

You may also like: All About the Singleton

There are mainly three concepts in which we can break the singleton property of a Singleton class in Java. In this post, we will discuss how it can break and how to prevent it.

Here is sample Singleton class and SingletonTest class.

Singleton.Java

package demo1;

public final class Singleton {

    private static volatile Singleton instance = null;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}


SingletonTest.java

  package demo1;

public class SingletonTest {
    public static void main(String[] args) {
        Singleton object1 = Singleton.getInstance();
        Singleton object2 = Singleton.getInstance();
        System.out.println("Hashcode of Object 1 - " + object1.hashCode());
        System.out.println("Hashcode of Object 2 - " + object2.hashCode());
    }
} 


Here is output; you can see it has the same hashcode for objectOne and objectTwo:

Hashcode of Object 1 - 1836019240
Hashcode of Object 2 - 1836019240


Now, we will break this pattern. First, we will use Java reflection.

Reflection

Java Reflection is an API used to examine or modify the behavior of methods, classes, and interfaces at runtime. Using the Reflection API, we can create multiple objects in the Singleton class. Consider the following example:

ReflectionSingleton.java

  package demo1;

import java.lang.reflect.Constructor;

public class ReflectionSingleton {
    public static void main(String[] args)  {

        Singleton objOne = Singleton.getInstance();
        Singleton objTwo = null;
        try {
            Constructor constructor = Singleton.class.getDeclaredConstructor();
            constructor.setAccessible(true);
            objTwo = (Singleton) constructor.newInstance();
        } catch (Exception ex) {
            System.out.println(ex);
        }

        System.out.println("Hashcode of Object 1 - "+objOne.hashCode());
        System.out.println("Hashcode of Object 2 - "+objTwo.hashCode());

    }
}   


This example shows how reflection can break the singleton pattern with Java reflect. You will get two hash codes, as shown below. It has a break on the singleton pattern.

Prevent Singleton Pattern From Reflection

There are many ways to prevent Singleton pattern from Reflection API, but one of the best solutions is to throw a run-time exception in the constructor if the instance already exists. In this, we can not able to create a second instance.

Deserialization

In serialization, we can save the object of a byte stream into a file or send over a network. Suppose if you serialize the Singleton class, and then again de-serialize that object, it will create a new instance, hence deserialization will break the Singleton pattern.

The below code is used to illustrate how the Singleton pattern breaks with deserialization.

Implements Serializable interface for Singleton Class.

DeserializationSingleton.Java

package demo1;

import java.io.*;

public class DeserializationSingleton {

    public static void main(String[] args) throws Exception {

        Singleton instanceOne = Singleton.getInstance();
        ObjectOutput out = new ObjectOutputStream(new FileOutputStream("file.text"));
        out.writeObject(instanceOne);
        out.close();

        ObjectInput in = new ObjectInputStream(new FileInputStream("file.text"));
        Singleton instanceTwo = (Singleton) in.readObject();
        in.close();

        System.out.println("hashCode of instance 1 is - " + instanceOne.hashCode());
        System.out.println("hashCode of instance 2 is - " + instanceTwo.hashCode());
    }

}


The output is below, and you can see two hashcodes.

hashCode of instance 1 is - 2125039532
hashCode of instance 2 is - 381259350


Prevent Singleton Pattern From Deserialization

To overcome this issue, we need to override readResolve() method in the Singleton class and return the same Singleton instance. Update Singleton.java, with below method.

   protected Object readResolve() { 
           return instance; 
     }  


Now, run the above DeserializationDemo class and see the output.

hashCode of instance 1 is - 2125039532
hashCode of instance 2 is - 2125039532


Cloning

Using the "clone" method, we can create a copy of the original object; it's the same thing if we applied clone in the singleton pattern. It will create two instances: one original and another one cloned object. In this case, we will break the Singleton principle, as shown in the below code.

Implement the " Cloneable" interface and override the clone method in the above Singleton class.

Singleton.java

    @Override
    protected Object clone() throws CloneNotSupportedException  {
        return super.clone();
    }


Then, test with cloning for breaking the singleton.

CloningSingleton.java

public class CloningSingleton {
    public static void main(String[] args) throws CloneNotSupportedException, Exception {
        Singleton instanceOne = Singleton.getInstance();
        Singleton instanceTwo = (Singleton) instanceOne.clone();
        System.out.println("hashCode of instance 1 - " + instanceOne.hashCode());
        System.out.println("hashCode of instance 2 - " + instanceTwo.hashCode());
    }

}


Here is the output:

hashCode of instance 1 - 1836019240
hashCode of instance 2 - 325040804


If we see the above output, two instances have different hashcodes. This means these instances are not the same.

Prevent Singleton Pattern From Cloning

In the above code, it breaks the Singleton principle, i. e created two instances. To overcome the above issue, we need to implement/override the clone() method and throw an exception CloneNotSupportedException from the clone method. If anyone tries to create a clone object of Singleton, it will throw an exception, as shown in the below code.

    @Override
    protected Object clone() throws CloneNotSupportedException  {
        throw new CloneNotSupportedException();
    }


Now, we can run the CloningSingleton class; it will throw CloneNotSupportedException while creating a clone object of the Singleton object.

Hope you found this post useful!

Further Reading

All About the Singleton

Singleton Pattern: A Deep Dive

Topics:
design pattern ,java ,singleton ,java 8 ,deserialization ,breaking ,serializable ,singleton pattern

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}