import java.io.Serializable;
public class Singleton implements Serializable,Cloneable{
private static final long serialVersionUID = 1L;
private static Singleton singleton=null;
//private static volatile Singleton singleton=null;
private Singleton() {
if(singleton!=null){
throw new RuntimeException("Its Singleton Class use getInstance method for object creation");
}
}
public static Singleton getInstance(){
return Holder.singleton;
}
/****
* good way for getting the instance. No need to worry about
* BillPughSingleton
*/
private static class Holder{
private static final Singleton singleton=new Singleton();
}
/***
/*
* Use this code for preventing Singleton breakage in multi threading scenario and comment above getInstance method
* As this is the efficient way
* If we put synchronized at method level level then will impact performance and will executed every time when getInstance is called
* But if once the instance is created then there is no need for synchronized.
*/
/* public static Singleton getInstance(){
if(singleton==null){
synchronized (Singleton.class) {
if(singleton==null){
singleton=new Singleton();
}
}
}
return singleton;
}*/
@Override
public Object clone() throws CloneNotSupportedException{
/***
* We can place below check OR we can remove the exception thrown check and return singleton instead of super.clone()
* Use any one way
*/
if(singleton!=null){
throw new RuntimeException("Its Singleton Class use getInstance method for object creation");
}
return super.clone();
}
/***
*
* To Prevent breaking of singleton pattern by using serilization/de serilization
*/
private Object readResolve(){
System.out.println("Read Resolve executed");
return singleton;
}
}
** ** Prueba Singleton
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/***
*
* Ways to prevent break Singleton
*/
public class Main {
private static ObjectInputStream inputStream;
public static void main(String[] args) throws Exception {
Singleton orginalSingletonObject = Singleton.getInstance();
/***
* Singleton is broken by using Reflection
* We can prevent that by putting a check in private constructor of Singleton.java
*
*/
breakSingletonByReflection(orginalSingletonObject);
/***
* By Serialization/De-Serialization break Singleton We need
* Serialization interface in a class needs to be serialized like
* Singleton.java
*
* To prevent breaking of singleton we can add readResolve method in Singleton.java
* readResolve is the method which returns the instance of the class when a serialized class is de serialized.
* So implement the readResolve method to return the same object.
* Hence prevent breaking of Singleton design pattern.
* Refer this link for more information on readResolve
* https://docs.oracle.com/javase/6/docs/platform/serialization/spec/input.html#5903
*/
breakSingletonByserialization(orginalSingletonObject);
/***
* By Cloning break Singleton
* We need to implement Cloneable interface
* We can prevent that by putting a check in clone method of Singleton.java
*/
breakSingletonByCloning(orginalSingletonObject);
/***
* Break Singleton By thread
* This scenario is related to multi-threading environment
* We can do this by putting double lock mechanism in Singleton.java and its good practice to use Volatile
* We can also prevent this scenario of breaking by creating object eagerly but its not good to create object eagerly
*/
breakSingletonByThreading(orginalSingletonObject);
}
private static void breakSingletonByThreading(Singleton orginalSingletonObject) {
ExecutorService executorService=Executors.newFixedThreadPool(2);
/**
* Run this code snippet after commenting the other code for better understanding
* Run it repeatly to create a condition when 2 threads enter the method getInstance() of Singleton class at a same time
* When 2 threads enter the getInstance method at same time they will get the singleton object as null (private static Singleton singleton in Singleton.java)
* Then they will create two different objects (have different hashcode) in this case singleton pattern will break.
*/
executorService.submit(Main::useSingleton); // JAVA 8 syntax it will get the singleton instance
executorService.submit(Main::useSingleton);
executorService.shutdown();
}
public static void useSingleton(){
Singleton singleton=Singleton.getInstance();
printSingletonData("By Threading", singleton);
}
private static void breakSingletonByCloning(Singleton orginalSingletonObject) throws CloneNotSupportedException {
Singleton clonedSingletonObject=(Singleton) orginalSingletonObject.clone();
printSingletonData("By Cloning", orginalSingletonObject, clonedSingletonObject);
}
private static void breakSingletonByReflection(Singleton orginalsingleton)
throws ClassNotFoundException, NoSuchMethodException,
InstantiationException, IllegalAccessException,
InvocationTargetException {
Class<?> singletonClass = Class.forName("SingletonTest.Singleton");
@SuppressWarnings("unchecked")
Constructor<Singleton> constructor = (Constructor<Singleton>) singletonClass
.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton s = constructor.newInstance();
printSingletonData("By Reflection", orginalsingleton, s);
}
private static void breakSingletonByserialization(Singleton orginalsingleton)
throws FileNotFoundException, IOException, ClassNotFoundException {
/**
* Serialization
*/
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("E:\\Singleton.ser"));
outputStream.writeObject(orginalsingleton);
outputStream.close();
/**
* DeSerialization
*/
inputStream = new ObjectInputStream(new FileInputStream("E:\\Singleton.ser"));
Singleton deserializeObject = (Singleton) inputStream.readObject();
deserializeObject.hashCode();
printSingletonData("By Serialization", orginalsingleton, deserializeObject);
}
public static void printSingletonData(String operationName,
Singleton orginalsingleton, Singleton reflectionSigletonObject) {
System.out.println("------------------------------------------");
System.out.println("New Operation");
System.out.println(operationName);
System.out.println("orginal Hashcode=" + orginalsingleton.hashCode());
System.out.println("New Object hashcode="
+ reflectionSigletonObject.hashCode());
Boolean value = orginalsingleton.hashCode() != reflectionSigletonObject.hashCode();
System.out.println("These Object have different hascode. They are two different object Right = "
+ value);
System.out.println("As these are different Object this means Singleton Pattern is broken");
}
private static void printSingletonData(String operationName,Singleton singleton) {
System.out.println("------------------------------------------");
System.out.println("New Operation");
System.out.println(operationName);
System.out.println("Object hashcode=" + singleton.hashCode());
//System.out.println("As these are different Object this means Singleton Pattern is broken");
}
}
La verdadera pregunta es por qué sería tan interesados en impedir que? Si un programador llega a esa cantidad de problemas, entonces significaría que intentará realizar ingeniería inversa del singleton de alguna manera para adaptarse a un caso especial que no fue considerado durante el diseño. No digo que sería la idea más brillante, pero al mismo tiempo, todo esto es hipotético de todos modos. –
1 muy bien presentada cuestión, especialmente el isómero [SSCCE] (http://pscode.org/sscce.html) – Bohemian