Singleton:如何通过Reflection停止创建实例

Tal*_*han 35 java reflection singleton

我知道在Java中,我们可以通过创建一个类的实例new,clone(),Reflectionserializing and de-serializing.

我创建了一个实现Singleton的简单类.

我需要一直停止创建我的类的实例.

public class Singleton implements Serializable{
    private static final long serialVersionUID = 3119105548371608200L;
    private static final Singleton singleton = new Singleton();
    private Singleton() { }
    public static Singleton getInstance(){
        return singleton;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException("Cloning of this class is not allowed"); 
    }
    protected Object readResolve() {
        return singleton;
    }
    //-----> This is my implementation to stop it but Its not working. :(
    public Object newInstance() throws InstantiationException {
        throw new InstantiationError( "Creating of this object is not allowed." );
    }
}
Run Code Online (Sandbox Code Playgroud)

在这个类我已经设法停止了类实例new,clone()并且serialization,但是我无法通过Reflection来阻止它.

我创建对象的代码是

try {
    Class<Singleton> singletonClass = (Class<Singleton>) Class.forName("test.singleton.Singleton");
    Singleton singletonReflection = singletonClass.newInstance();
} catch (ClassNotFoundException e) {
    e.printStackTrace();
} catch (InstantiationException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
}
Run Code Online (Sandbox Code Playgroud)

Dav*_*e G 57

尝试创建公共构造函数

private Singleton() {
    if( Singleton.singleton != null ) {
        throw new InstantiationError( "Creating of this object is not allowed." );
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这种方法存在一个缺陷,如果我们在任何`getInstance`调用(使用lazy init)之前使用反射创建实例,该怎么办?仍然允许反射继续进行,因为此时静态变量仍为空. (8认同)
  • 哇...伟大的思想相似...... 3个答案几乎相同......歇斯底里. (2认同)
  • @ringbearer当您尝试创建类的实例时,无论是通过反射还是直接创建,该类都将被初始化,包括执行字段初始值设定项。是否调用“getInstance”并不重要。由于字段初始值设定项首先出现,因此此方法将拒绝任何反射实例化尝试。 (2认同)
  • 不完全是在类加载时(因为未准确指定加载时间),但绝对是在创建实例之前[按指定](https://docs.oracle.com/javase/specs/jls/se17/html /jls-12.html#jls-12.4.1)。这对于你的方法来说已经足够了。看到三个评论错误地声称这不起作用,这很烦人。 (2认同)

Arn*_*sch 18

像这样定义单例:

public enum Singleton {
    INSTANCE
}
Run Code Online (Sandbox Code Playgroud)


Jon*_*eet 15

检查构造函数怎么样:

private Singleton() {
    if (singleton != null) {
        throw new IllegalStateException("Singleton already constructed");
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,这可能并没有真正阻止它 - 如果有人正在使用反射来访问私有成员,他们可能能够将字段设置为null.你必须问自己,你想要阻止什么,以及它是多么有价值.

(编辑:正如Bozho提到的那样,即使通过反思也可能无法设置最终字段.如果有一些方法可以通过JNI等进行,我不会感到惊讶,但是......如果你给人们足够的访问权限,他们就能够做几乎任何事......)


Boz*_*zho 10

private Singleton() { 
    if (Singleton.singleton != null) {
        throw new RuntimeException("Can't instantiate singleton twice");
    }
}
Run Code Online (Sandbox Code Playgroud)

你应该注意的另一件事是readResolve(..)方法,因为你的类实现了Serialiable.你应该返回现有的实例.

但使用单身人士的最简单方法是通过枚举 - 你不必担心这些事情.

  • @HrishikeshMishra:不知道你的意思,但我尝试了你展示的*精确*代码。请注意,在任何合理的虚拟机中,该类将在您调用构造函数之前初始化...... (2认同)