2

我正在研究单例,我开发了一个非常基本的单例类..

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());
    }
}

请告知其他可以破解单例的方法..!!

4

6 回答 6

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());
于 2012-07-25T17:08:19.537 回答
2

如果您有两个类加载器,您将能够从每个类加载器创建一个单例。

文档“什么时候是单例而不是单例”也值得一读。

于 2012-07-25T17:05:11.410 回答
1

我的回答是:

为什么这有关系?

如果您正在尝试设计安全、不可破解的代码,那么单例不是解决方案。它旨在强制普通开发人员使用它的系统实例。所有这些绕过它的方法都需要大量额外的工作,而人们不会仅仅为了使用该类的不同实例而做这些工作。

于 2012-07-25T17:19:57.937 回答
1

在某些情况下,Singleton 的行为并不像。

1.如果Singleton Classes被Garbage Collection销毁,则重新加载。
2.多个虚拟机中的多个单
例 3.不同类加载器同时加载多个单例。
4经过序列化和反序列化的Singleton对象的副本

于 2016-02-01T06:10:26.550 回答
0

通过反射,设置ref = null. 通过将其重新分配为null,懒惰地构造单例的逻辑将在下一次调用 时再次触发getSingletonObject

于 2012-07-25T17:03:18.043 回答
0

您可以使用字节码工程库将单例构造函数公开。

此外,在一些较旧的 Java 版本中(这曾经在 1.3 中工作),您可以简单地创建一个具有相同名称的类,使用公共构造函数并针对该类进行编译。在运行时,这允许您创建真实类的实例(此漏洞已在以后的 JRE 版本中的字节码验证中修复)。

于 2012-07-25T17:08:47.693 回答