Understand the prevention of breaking the singleton class pattern thoroughly

Posted by Ads on Mon, 23 Dec 2019 21:23:29 +0100

In Sharing Learning from Excellent Lessons, we explored three main ways to destroy the properties of a single case and how to prevent it.Share it for your reference.

We are accustomed to using the singleton design pattern in our applications when needed.It is well known that in a single design pattern, we can only create one instance and access it throughout the application.In some cases, however, it will break the singleton behavior.
In three main concepts, we can break the singleton property of the Singleton class in Java.In this article, we will discuss how to destroy it and how to prevent it.
These are the sample Singleton and SingletonTest classes.
Singleton.

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());
    }
}

This is the output; you can see that it has the same hashcode as objectOne and objectTwo:

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

Now, we're going to break that pattern.First, we'll use Java reflection.

reflex

Java Reflection is an API for checking or modifying the behavior of methods, classes, and interfaces at run time.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 breaks the singleton pattern with Java reflection.You will get two hash codes, as shown below.It breaks through the singleton pattern.

Prevent singleton mode reflection

There are many ways to prevent reflecting the Singleton pattern in the API, but one of the best solutions is to raise a runtime exception in the constructor if an instance already exists.In this case, we cannot create a second instance.

Deserialize

In serialization, we can save the object of the byte stream to a file or send it over the network.Assuming you serialize the Singleton class and then deserialize the object again, it will create a new instance, so deserialization will break the Singleton pattern.

The following code illustrates how a single inverse pattern is interrupted by deserialization.
Implement the Serializable interface for the 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 as follows, and you can see two hashcodes.

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

Prevent Singleton Mode Deserialization

To overcome this problem, we need to override the readResolve() method in the Singleton class and return the same Singleton instance.Update Singleton.java using the following methods.

 protected Object readResolve() { 
           return instance; 
     }

Now run the DeserializationDemo class above and view the output.

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

Clone

Using the Clone method, we can create a copy of the original object; this is the same thing if we apply cloning in the singleton mode.It creates two instances: one and the other.In this case, we'll break the Singleton principle, as shown in the code below.

Implement the Clonable interface and override the clone method in the Singleton category above

Singleton.java

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

Then, test the clone to break 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());
    }
}

This is the output:

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

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

Prevent single-case pattern cloning

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

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

Now we can run the loningSingleton class; when creating a cloned object of a single object, it will throw CloneNotSupportedException.

This article is written here. If there are any deficiencies, you are welcome to add comments. I hope this article is useful to you!

Topics: Java network