Prevent Singleton Pattern from Reflection, Serialization and Cloning

Prevent Singleton Pattern from Reflection, Serialization and Cloning

There are three way which can break singleton property of a class in java. Let’s discuss them one by one.
1. Reflection: Reflection can be help to destroy singleton property of singleton class, as shown in following example:

import java.lang.reflect.Constructor;
class Singleton {
public static Singleton instance = new Singleton();
private Singleton() {
// private constructor
}
}
public class MainClass {
public static void main(String[] args) {
Singleton instance1 = Singleton.instance;
Singleton instance2 = null;
try {
Constructor[] constructors = Singleton.class
.getDeclaredConstructors();
for (Constructor constructor : constructors) {
// Below code will destroy the singleton pattern
constructor.setAccessible(true);
instance2 = (Singleton) constructor.newInstance();
break;
}
}
catch (Exception e) {
e.printStackTrace();
}
System.out.println(“instance1.hashCode():- ” + instance1.hashCode());
System.out.println(“instance2.hashCode():- ” + instance2.hashCode());
}
}
/*
* Output:- instance1.hashCode():- 366712642 instance2.hashCode():- 1829164700
*/

How to fix Reflection issue: To fix issue raised by reflection, enums are used because java ensures internally that enum value is instantiated only once. Since java Enums are globally accessible, they can be used for singletons. Its only drawback is that it is not flexible i.e it does not allow lazy initialization.

public enum GraassTech{
INSTANCE;
}

As enums doesn’t have any constructor so it is not possible for Reflection to utilize it. Enums have their by-default constructor, we can’t invoke them. JVM it self handles the creation and invocation of enum constructors internally. As enums doesn’t give their constructor definition to the program, it is not possible for us to access them by Reflection also. Hence, reflection can’t break singleton property in case of enums.
2. Serialization:- Serialization can also lead breakage of singleton property of singleton classes. Serialization is used to convert an object into byte stream and save in a file or send over a network. Suppose you serialize an object of a singleton class. Then if you de-serialize that object it will create a new instance and hence break the singleton pattern.

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.Serializable;
class Singleton implements Serializable {
// public instance initialized when loading the class
public static Singleton instance = new Singleton();
private Singleton() {
// private constructor
}
}
public class GraassTech {
public static void main(String[] args) {
try {
Singleton instance1 = Singleton.instance;
ObjectOutput out = new ObjectOutputStream(new FileOutputStream(
“file.text”));
out.writeObject(instance1);
out.close();
// deserailize from file to object
ObjectInput in = new ObjectInputStream(new FileInputStream(
“file.text”));
Singleton instance2 = (Singleton) in.readObject();
in.close();
System.out.println(“instance1 hashCode:- ” + instance1.hashCode());
System.out.println(“instance2 hashCode:- ” + instance2.hashCode());
}
catch (Exception e) {
e.printStackTrace();
}
}
}
/*
* Output:- instance1 hashCode:- 1550089733 instance2 hashCode:- 865113938
*/

How to fix serialization issue:- To fix this issue, we have to implement method readResolve() method of serialization interface.

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.Serializable;
class Singleton implements Serializable {
// public instance initialized when loading the class
public static Singleton instance = new Singleton();
private Singleton() {
// private constructor
}
protected Object readResolve() {
return instance;
}
}
public class GraassTech {
public static void main(String[] args) {
try {
Singleton instance1 = Singleton.instance;
ObjectOutput out = new ObjectOutputStream(new FileOutputStream(
“file.text”));
out.writeObject(instance1);
out.close();
// deserailize from file to object
ObjectInput in = new ObjectInputStream(new FileInputStream(
“file.text”));
Singleton instance2 = (Singleton) in.readObject();
in.close();
System.out.println(“instance1 hashCode:- ” + instance1.hashCode());
System.out.println(“instance2 hashCode:- ” + instance2.hashCode());
}
catch (Exception e) {
e.printStackTrace();
}
}
}
/*
* Output:- instance1 hashCode:- 1550089733 instance2 hashCode:-1550089733
*/

2. Cloning: Cloning is a concept to create duplicate objects. Using clone mechanism we can create copy of object. Suppose, we ceate clone of a singleton object, then it will create a copy of that singleton class.

class SuperClass implements Cloneable {
int i = 10;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
// Singleton class
class Singleton extends SuperClass {
// public instance initialized when loading the class
public static Singleton instance = new Singleton();
private Singleton() {
// private constructor
}
}
public class GraassTech {
public static void main(String[] args) throws CloneNotSupportedException {
Singleton instance1 = Singleton.instance;
Singleton instance2 = (Singleton) instance1.clone();
System.out.println(“instance1 hashCode:- ” + instance1.hashCode());
System.out.println(“instance2 hashCode:- ” + instance2.hashCode());
}
}/*
* Output :- instance1 hashCode:- 366712642 instance2 hashCode:- 1829164700
*/

How to fix Cloning issue:- To fix this issue, override clone() method and return same instance from clone method. Now whenever user will try to create clone of singleton object, it will return same instance and hence our class remains singleton.

class SuperClass implements Cloneable {
int i = 10;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
// Singleton class
class Singleton extends SuperClass {
// public instance initialized when loading the class
public static Singleton instance = new Singleton();
private Singleton() {
// private constructor
}
@Override
protected Object clone() throws CloneNotSupportedException {
return instance;
}
}
public class GraassTech {
public static void main(String[] args) throws CloneNotSupportedException {
Singleton instance1 = Singleton.instance;
Singleton instance2 = (Singleton) instance1.clone();
System.out.println(“instance1 hashCode:- ” + instance1.hashCode());
System.out.println(“instance2 hashCode:- ” + instance2.hashCode());
}
}/*
* Output :- instance1 hashCode:- 366712642 instance2 hashCode:- 366712642
*/

%d bloggers like this: