java单例:如何通过反射停止创建实例
我知道在Java中,我们可以通过new
、clone()
、Reflection
和serializing and de-serializing
创建类的实例
我创建了一个简单的类来实现一个单例
我需要停止所有创建我的类实例的方法
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." );
}
}
在这个类中,我通过new
、clone()
和serialization
成功地停止了类实例,但是我无法通过反射来停止它
我创建对象的代码是
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();
}
# 1 楼答案
通过在私有构造函数中添加以下检查
# 2 楼答案
您应该注意的另一件事是
readResolve(..)
方法,因为您的类实现了Serialiable
。在那里,您应该返回现有实例但是使用单例的最简单方法是通过枚举-您不必担心这些事情
# 3 楼答案
作为单例的替代方案,您可以查看monostate pattern。然后,类的实例化不再是问题,您不必担心所列出的任何场景
在monostate模式中,类中的所有字段都是
static
。这意味着类的所有实例共享相同的状态,就像单例一样。此外,这一事实对呼叫者是透明的;他们不需要知道像getInstance
这样的特殊方法,他们只是创建实例并使用它们但是,就像单例一样,它是一种隐藏的全局状态;这是非常糟糕的
# 4 楼答案
除了enum解决方案之外,所有其他解决方案都可以通过反射解决 以下是关于如何绕过Dave G解决方案的两个示例:
1:设置变量Singleton。单例到空值
输出:
2:不调用getInstance
输出:
因此,如果您不想使用枚举,我可以想出两种方法:
第一个选项:使用securityManager:
它防止使用未经授权的操作(从类外部调用私有方法…)
因此,您只需要在其他答案提出的单例构造函数中添加一行
它所做的是防止调用
setAccessible(true)
所以当你想叫它的时候:此验证将发生:
java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "createSecurityManager")
第二个选项:在singleton构造函数中,测试调用是否通过反射进行:
我建议您参考另一个Stackoverflow thread,以了解获取调用方类或方法的最佳方法
因此,如果我在Singleton构造函数中添加这个:
我这样称呼它:
输出将是:
jdk.internal.reflect.DelegatingConstructorAccessorImpl
但如果我定期调用它(实例化公共构造函数或在没有反射的情况下调用方法),则会打印调用方法的类的名称。例如,我有:
callerClassName将是
MainReflexion
,因此输出将是MainReflexion
PS:如果提议的解决方案存在变通办法,请让我知道
# 5 楼答案
如下定义单例:
# 6 楼答案
签入构造函数如何:
当然,这可能不是真正的停止-如果有人在混淆视听以访问私有成员,他们可以自己将字段设置为null。你必须问问自己,你想阻止什么,这有多值得
(编辑:正如Bozho所提到的,即使通过反射,最终字段也可能无法设置。如果有一些方法通过JNI等进行设置,我不会感到惊讶……如果你给人们足够的访问权限,他们几乎可以做任何事……)