While Implementing Singleton the following things should be answered
- Reflection
- Serialization
- Cloning
Objects for Singleton Classes implemented using private Constructor can be invoked by Reflection as below
Item3.java
package com.mugil.org.ej;
import java.lang.reflect.Constructor;
public class Item3 {
public static void main(String[] args)
{
// reflection concept to get constructor of a Singleton class.
Constructor<Singleton> constructor;
try {
constructor = Singleton.class.getDeclaredConstructor();
// change the accessibility of constructor for outside a class object creation.
constructor.setAccessible(true);
// creates object of a class as constructor is accessible now.
Singleton secondOb = constructor.newInstance();
System.out.println(secondOb.getName());
// close the accessibility of a constructor.
constructor.setAccessible(false);
} catch (Exception e){
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class Singleton {
private static Singleton instance = new Singleton();
/* private constructor */
private Singleton() {}
public static Singleton getDefaultInstance() {
return instance;
}
public String getName()
{
return "MyName";
}
}
Output
MyName
Singleton and Serialization
Without readResolve() Method
// Java code to explain effect of
// Serilization on singleton classes
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 GFG
{
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:- 785945
With readResolve() Method
// Java code to remove the effect of
// Serialization on singleton classes
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
}
// implement readResolve method
protected Object readResolve()
{
return instance;
}
}
public class GFG
{
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
Refer https://codethataint.com/blog/singleton-and-serialization/
// JAVA code to explain cloning
// issue with 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
}
}
public class GFG
{
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
Output :-
instance1 hashCode:- 366712642
instance2 hashCode:- 1829164700
Two different hashCode means there are 2 different objects of singleton class.
To overcome this issue, override clone() method and throw an exception from clone method that is CloneNotSupportedException. Now whenever user will try to create clone of singleton object, it will throw exception and hence our class remains singleton.
// JAVA code to explain overcome
// cloning issue with 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
{
throw new CloneNotSupportedException();
}
}
public class GFG
{
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
Output:-
Exception in thread "main" java.lang.CloneNotSupportedException
at GFG.Singleton.clone(GFG.java:29)
at GFG.GFG.main(GFG.java:38)
If you don;t want to throw exception you can also return the same instance from clone method.
// JAVA code to explain overcome
// cloning issue with 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 GFG
{
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
Output:-
instance1 hashCode:- 366712642
instance2 hashCode:- 366712642
The Best way to implement Singleton is by using ENUM which takes care of Serialization and Other Issues on its own.