我正在研究单身人士,我已经开发了一个非常基本的单身人士课程。
public class SingletonObject {
private static SingletonObject ref;
private SingletonObject () //private constructor
{ }
public static synchronized SingletonObject getSingletonObject()
{
if (ref == null)
ref = new SingletonObject();
return ref;
}
public Object clone() throws CloneNotSupportedException
{throw new CloneNotSupportedException ();
}
}
现在,下面是我破解单身人士的一种方式..
public class CrackingSingleton {
public static void main(String[] args) throws ClassNotFoundException,
IllegalArgumentException, SecurityException,
InstantiationException, IllegalAccessException,
InvocationTargetException {
//First statement retrieves the Constructor object for private constructor of SimpleSingleton class.
Constructor pvtConstructor = Class.forName("CrackingSingleton.SingletonObject").getDeclaredConstructors()[0];
//Since the constructor retrieved is a private one, we need to set its accessibility to true.
pvtConstructor.setAccessible(true);
//Last statement invokes the private constructor and create a new instance of SimpleSingleton class.
SingletonObject notSingleton1 = ( SingletonObject) pvtConstructor.newInstance(null);
System.out.println(notSingleton1.hashCode());
System.out.println("notSingleton1 --->"+notSingleton1.toString());
SingletonObject notSingleton2 = ( SingletonObject) pvtConstructor.newInstance(null);
System.out.println("notSingleton2 --->"+notSingleton2.hashCode());
System.out.println(notSingleton2.toString());
}
}
请告知其他单身人士破裂的方法.. !!
答案 0 :(得分:5)
我能想到的三种方式是:
如果您的单例类是可序列化的,那么您可以序列化它的一个实例,然后反序列化它并获得该类的第二个对象。
您可以通过实施readResolve方法来避免这种情况。
public class Singleton implements Serializable {
private static final Singleton INSTANCE = new Singleton();
public static Singleton getInstance(){
return INSTANCE;
}
public Object readResolve() throws ObjectStreamException {
return INSTANCE; //ensure singleton is returned upon deserialization.
}
}
同一个类可以由两个不同的类加载器加载,因此,您可以通过在由两个不同的类加载器加载的类中调用其getInstance
方法来创建单个类的两个实例。这种方法可以工作,而不必诉诸于违反私有构造函数。
ClassLoader cl1 = new URLClassLoader(new URL[]{"singleton.jar"}, null);
ClassLoader cl2 = new URLClassLoader(new URL[]{"singleton.jar"}, null);
Class<?> singClass1 = cl1.loadClass("hacking.Singleton");
Class<?> singClass2 = cl2.loadClass("hacking.Singleton");
//...
Method getInstance1 = singClass1.getDeclaredMethod("getInstance", ...);
Method getInstance2 = singClass2.getDeclaredMethod("getInstance", ...);
//...
Object singleton1 = getInstance1.invoke(null);
Object singleton2 = getInstance2.invoke(null);
正如您已经指出的那样,通过反射,您可以创建该类的两个实例。我认为前面的例子只是同一种方法的一种变体。但我相信你可以使用SecurityManager
阻止这两者发生。
System.setSecurityManager(new SecurityManager());
答案 1 :(得分:2)
如果你有两个类加载器,你将能够从每个类加载器创建一个单例。
答案 2 :(得分:1)
我的回答是:
为什么重要?
如果您正在尝试设计安全,无法破解的代码,那么Singleton就不是解决方案。它旨在强制普通开发人员使用它的系统实例。所有这些绕过它的方法都需要大量的额外工作,而有些人不会仅仅使用不同的类实例。
答案 3 :(得分:1)
在某些情况下,Singleton的行为并不像。
1.如果单例类被垃圾收集破坏,则重新加载。
2.多个虚拟机中不止一个单身人士
3.不同类型装载机同时装载的单个单人
具有经过序列化和反序列化的Singleton对象的4Copies
答案 4 :(得分:0)
通过反射,设置ref = null
。通过将其重新指定为null
,将在下次调用getSingletonObject
时再次触发延迟构造单例的逻辑。
答案 5 :(得分:0)
您可以使用字节码工程库公开单例构造函数。
此外,在一些较旧的Java版本中(这曾经在1.3中工作),您可以简单地创建一个具有相同名称的类,使用公共构造函数和针对该类的编译。在运行时,这允许您创建真实类的实例(此漏洞已在以后的JRE版本的字节码验证中得到修复)。