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

use*_*323 23 java singleton

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

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);
}
Run Code Online (Sandbox Code Playgroud)

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

谢谢.

lah*_*her 16

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

public static enum BookingFactory {
    INSTANCE;
    public static BookingFactory getInstance() {
        return INSTANCE;
    }
}
Run Code Online (Sandbox Code Playgroud)

您无法通过反射创建枚举.

getInstance()方法是多余的,但是更容易运行测试,抛出以下异常:

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

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


Boh*_*ian 14

在构造函数中进行断言:

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

  • @Kaunteya 是的,当与问题中的代码一起使用时,它是线程安全的,因为`instance` 是在一个静态块中创建的,它在类加载时执行(不是延迟初始化),并且类加载器内置了自己的线程安全。 JVM 保证在可以使用类之前完成所有静态初始化,包括调用构造函数。 (2认同)

sim*_*ico 9

改编自使用延迟加载时制作Java Singleton反射证明:

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;
  }
Run Code Online (Sandbox Code Playgroud)

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

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

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