我有一个像这样的单身人士。
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);
}
当我运行它时,我看到了多条打印输出消息。有没有办法阻止这个单例从这个反射中被多次实例化?
感谢。
答案 0 :(得分:16)
尝试使用enum
。 enums是一个很好的单身人士。
public static enum BookingFactory {
INSTANCE;
public static BookingFactory getInstance() {
return INSTANCE;
}
}
您无法通过反射创建枚举。
getInstance()方法是多余的,但是更容易运行测试,抛出以下异常:
java.lang.IllegalArgumentException: Cannot reflectively create enum objects
at java.lang.reflect.Constructor.newInstance(Constructor.java:530)
at MultiSingletonTest.main(MultiSingletonTest.java:40)
哦,看,有人已经给了回答。无论如何要张贴更完整。
答案 1 :(得分:14)
在构造函数中进行断言:
private BookingFactory() {
if (instance != null)
throw new IllegalStateException("Only one instance may be created");
System.out.println("Object is created.");
}
答案 2 :(得分:8)
改编自Making the Java Singleton Reflection Proof when using Lazy Loading:
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<Object>() {
public Object run() {
INSTANCE= new JavaSingleton();
return null;
}
});
}
return INSTANCE;
}
构造函数检查调用者是否可以访问它。正如链接所解释的那样,需要创建一个允许Singleton类本身调用构造函数的策略文件。
Bohemian抛出异常的方法并不会阻止客户端在调用getInstance()之前反射性地调用构造函数。即使它确保只创建一个实例,也不能保证这是由Singleton类'getInstance()
方法完成的。
访问控制检查将阻止此不必要的实例化。
答案 3 :(得分:4)
我强烈建议阅读What is an efficient way to implement a singleton pattern in Java? - 使用枚举来阻止您所描述的内容,并且是在java中实现单例的推荐方法。
答案 4 :(得分:1)
如果您的单身实际上并不存储状态,那么您最好的选择是不使用单身。相反,将工厂实现为静态无状态方法。
答案 5 :(得分:1)
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<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");
}
}