There are three main ways in which the singleton property of a class can be violated. In this post, we will discuss how each of these scenarios can break the singleton pattern and how to prevent them.
- Reflection
Consider the following example, which demonstrates how reflection can break the singleton pattern:
Singleton.java,package com.example.demo;
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
package com.example.demo;
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());
}
}
Output:-
Hashcode of Object 1 - 366712642
Hashcode of Object 2 - 1829164700
RuntimeException from the constructor if an instance of the class already exists. This prevents the creation of a second instance, even through reflection.private Singleton() throws Exception {
if (instance != null) {
throw new RuntimeException("Instance already exists");
}
}
- Deserialization
The following example demonstrates how serialization and deserialization can break the Singleton Design Pattern:
package com.example.demo;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
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 instanceOne is - " + instanceOne.hashCode());
System.out.println("hashCode of instanceTwo is - " + instanceTwo.hashCode());
}
}
Output:-
hashCode of instanceOne is - 1550089733
hashCode of instanceTwo is - 2003749087
instanceOne and instanceTwo are different. Therefore, the Singleton Design Pattern is broken.Prevent Singleton Pattern from Deserialization:-
instanceOne and instanceTwo are different. Therefore, the Singleton Design Pattern has been broken.Singleton.java,
@override
protected Object readResolve() {
return instance;
}
Now run the DeserializationDemo class and observe the output:
hashCode of instanceOne is - 1550089733
hashCode of instanceTwo is - 1550089733
- Cloning
clone() method, we can create a copy of an existing object. Similarly, if cloning is applied to a singleton class, it creates a second instance in addition to the original object. As a result, the Singleton Design Pattern is broken, as shown in the following example.To demonstrate this, implement the Cloneable interface and override the clone() method in the singleton class.
.....
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
....
Main Class,
package com.example.demo;
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 instance1 - "+instanceOne.hashCode());
System.out.println("hashCode of instance2 - "+instanceTwo.hashCode());
}
}
Output:-
hashCode of instance1 - 366712642
hashCode of instance2 - 1829164700
Prevent Singleton Pattern from Cloning:-
clone() method and throw a CloneNotSupportedException. If anyone attempts to create a clone of the singleton object, the clone() method will throw this exception, as shown in the following code. ......
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
........
Now run the main class. When it attempts to create a clone of the singleton object, a CloneNotSupportedException will be thrown.
Thank you for visiting the blog!
Related Posts:--
1) Singleton Design Pattern in java with example
2) Factory Design Pattern in Java
3) Builder Design Pattern in Java
No comments:
Post a Comment