java单例-通过反射防止多次创建

java单例-通过反射防止多次创建,java,singleton,Java,Singleton,我有一个这样的单身汉 public class BookingFactory { private final static BookingFactory instance; static { instance = new BookingFactory(); } public static BookingFactory getInstance() { return instance; } private Boo

我有一个这样的单身汉

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);
}
公共类预订工厂{
私有最终静态BookingFactory实例;
静止的{
实例=新建BookingFactory();
}
公共静态BookingFactory getInstance(){
返回实例;
}
私人书厂(){
System.out.println(“对象已创建”);
}
}
公开课考试{
BookingFactory实例=BookingFactory.getInstance();
instance=BookingFactory.getInstance();
Class clazz=Class.forName(“com.test.BookingFactory”);
构造函数pvtConstructor=clazz.getDeclaredConstructors()[0];
//设置其访问控制
pvtConstructor.setAccessible(true);
//调用私有构造函数
BookingFactory notSingleton=(BookingFactory)pvtConstructor.newInstance(null);
}
当我运行这个程序时,我看到了不止一条打印出来的消息。是否有任何方法可以防止此单例从该反射中多次实例化


谢谢。

在构造函数中进行断言:

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

我强烈建议阅读-使用枚举可以避免您所描述的内容,并且是在java中实现单例的推荐方法。

尝试使用
枚举。枚举是很好的单例

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)

哦,看,有人已经给出了枚举的答案。无论如何都要发布以获得更多完整性。

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

改编自:

包服务器;
导入java.lang.reflect.ReflectPermission;
导入java.security.*;
公共类JavaSingleton{
私有静态JavaSingleton实例=null;
私有JavaSingleton(){
ReflectPermission perm=新的ReflectPermission(“suppressAccessChecks”,“suppressAccessChecks”);
AccessController.checkPermission(perm);
}
同步的公共静态最终JavaSingleton getInstance(){
if(实例==null){
AccessController.doPrivileged(新的PrivilegedAction(){
公共对象运行(){
INSTANCE=newjavasingleton();
返回null;
}
});
}
返回实例;
}
构造函数有一个检查,看看调用方是否有权访问它。正如链接所解释的,需要创建一个允许Singleton类本身调用构造函数的策略文件

Bohemian抛出异常的方法并不阻止客户端在调用getInstance()之前反射调用构造函数。即使它确保只创建一个实例,也不能保证这是由Singleton类“
getInstance()
方法完成的

访问控制检查将防止这种不必要的实例化

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");

}

}
import java.io.FileInputStream;
导入java.io.FileNotFoundException;
导入java.io.FileOutputStream;
导入java.io.IOException;
导入java.io.ObjectInputStream;
导入java.io.ObjectOutputStream;
导入java.lang.reflect.Constructor;
导入java.lang.reflect.InvocationTargetException;
导入java.util.concurrent.ExecutorService;
导入java.util.concurrent.Executors;
/***
* 
*防止单胎破裂的方法
*/
公共班机{
私有静态对象inputStream inputStream;
公共静态void main(字符串[]args)引发异常{
Singleton orginalSingletonObject=Singleton.getInstance();
/***
*Singleton是通过使用反射来打破的
*我们可以通过检查Singleton.java的私有构造函数来防止这种情况
* 
*/
breakSingletonByReflection(原始SingleTonObject);
/***
*通过序列化/反序列化打破我们需要的单例
*类中的序列化接口需要像
*Singleton.java
* 
*为了防止破坏singleton,我们可以在singleton.java中添加readResolve方法
*readResolve是在序列化类反序列化时返回该类实例的方法。
*因此,实现readResolve方法以返回相同的对象。
*因此,防止打破单例设计模式。
*有关readResolve的更多信息,请参阅此链接
*  https://docs.oracle.com/javase/6/docs/platform/serialization/spec/input.html#5903
*/
breakSingletonByserialization(orginalSingletonObject);
/***
*通过克隆打破单身
*我们需要实现可克隆的接口
*我们可以通过检查Singleton.java的clone方法来防止这种情况
*/
breakSingletonByCloning(orginalSingletonObject);
/***
*断线
*此场景与多线程环境相关
*我们可以通过在Singleton.java中加入双锁机制以及使用Volatile的良好实践来实现这一点
*我们还可以通过急切地创建对象来防止这种情况,但急切地创建对象并不好
*/
breakSingletonByThreading(orginalSingletonObject);
}
私有静态void breakSingletonByThreading(Singleton orginalSingletonObject){
ExecutorService ExecutorService=Executors.newFixedThreadPool(2);
/**
*在注释其他代码后运行此代码段,以便更好地理解
*当两个线程同时进入Singleton类的getInstance()方法时,重复运行它以创建一个条件
*当两个线程同时进入getInstance方法时,它们会将singleton对象获取为null(singleton.java中的私有静态singleton singleton)
*然后,他们将创建两个不同的对象(具有不同的哈希代码),在这种情况下,单例模式将中断。
*/
executorService.submit(Main::useSingleton);//Java8语法它将获得singleton实例
executorService.submit(Main::useSingleton)
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");

}

}