java Singleton – 通过reflection防止多次创建

我有一个像这样的单身人士。

public class BookingFactory { private final static BookingFactory instance; static { instance = new BookingFactory(); } public static BookingFactory getInstance() { return instance; } private BookingFactory() { System.out.println("Object is created."); } } public class Test { BookingFactory instance = BookingFactory.getInstance(); instance = BookingFactory.getInstance(); Class clazz = Class.forName("com.test.BookingFactory"); Constructor pvtConstructor = clazz.getDeclaredConstructors()[0]; // Set its access control pvtConstructor.setAccessible(true); // Invoke Private Constructor BookingFactory notSingleton = (BookingFactory) pvtConstructor.newInstance(null); } 

当我运行它时,我看到了多条打印输出消息。 有没有办法阻止这个单例从这个reflection中被多次实例化?

谢谢。

尝试使用enum 。 enums是一个很好的单身人士。

 public static enum BookingFactory { INSTANCE; public static BookingFactory getInstance() { return INSTANCE; } } 

您无法通过reflection创建枚举。

getInstance()方法是多余的,但可以更轻松地运行测试,抛出以下exception:

 java.lang.IllegalArgumentException: Cannot reflectively create enum objects at java.lang.reflect.Constructor.newInstance(Constructor.java:530) at MultiSingletonTest.main(MultiSingletonTest.java:40) 

哦,看,有人已经给出了枚举答案。 无论如何都要张贴更完整。

在构造函数中进行断言:

 private BookingFactory() { if (instance != null) throw new IllegalStateException("Only one instance may be created"); System.out.println("Object is created."); } 

改编自使用延迟加载时制作Java Singletonreflectioncertificate :

 package server; import java.lang.reflect.ReflectPermission; import java.security.*; public class JavaSingleton { private static JavaSingleton INSTANCE = null; private JavaSingleton() { ReflectPermission perm = new ReflectPermission("suppressAccessChecks", ""); AccessController.checkPermission(perm); } synchronized public static final JavaSingleton getInstance() { if (INSTANCE == null) { AccessController.doPrivileged(new PrivilegedAction() { public Object run() { INSTANCE= new JavaSingleton(); return null; } }); } return INSTANCE; } 

构造函数检查调用者是否可以访问它。 正如链接所解释的那样,需要创建一个允许Singleton类本身调用构造函数的策略文件。

抛出exception的方法不会阻止客户端在调用getInstance()之前reflection性地调用构造函数。 即使它确保只创建一个实例,也不能保证这是由Singleton类的’ getInstance()方法完成的。

访问控制检查将阻止此不必要的实例化。

我强烈建议阅读什么是在Java中实现单例模式的有效方法? – 使用枚举可以防止您所描述的内容,并且是在java中实现单例的推荐方法。

如果您的单身实际上没有存储状态,那么您最好的选择是不使用单身。 相反,将工厂实现为静态无状态方法。

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

**测试单身人士**

 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 constructor = (Constructor) 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"); } }