Java中反射和Unsafe破坏单例设计模式

有如下单例模式设计代码:

class Singleton
{
    private String info = "HELLO SHIT";

    private static Singleton instance;

    private Singleton()
    {
        System.out.println("******实例化对象******");
    }

    public static Singleton getInstance()
    {
        synchronized (Singleton.class)
        {
            if (instance == null)
            {
                instance = new Singleton();
            }
        }
        return instance;
    }

    public void show()
    {
        System.out.println("www.google.com");
    }
}

按照规则,我们只能获取一个实例化的对象,如下面的代码:

public class Hello
{
    public static void main(String[] args) throws Exception
    {
        Singleton instanceA = Singleton.getInstance();
        Singleton instanceB = Singleton.getInstance();
        System.out.println(instanceA.hashCode());
        System.out.println(instanceB.hashCode());
        System.out.println(instanceA == instanceB);
    }
}

程序输出:

******实例化对象******
685325104
685325104
true

Process finished with exit code 0

可以看到instanceA和instanceB完全相同.

下面演示用反射获取单例的构造函数,并且实例化出多个对象:

public class Hello
{
    public static void main(String[] args) throws Exception
    {
        Constructor c = Singleton.class.getDeclaredConstructor();
        c.setAccessible(true);

        Singleton instanceA = (Singleton)c.newInstance();
        Singleton instanceB = (Singleton)c.newInstance();
        System.out.println(instanceA.hashCode());
        System.out.println(instanceB.hashCode());
        System.out.println(instanceA == instanceB);
    }
}

程序输出:

******实例化对象******
******实例化对象******
685325104
460141958
false

Process finished with exit code 0

可以看到,这里调用了两次构造函数,实例化了两个不同的Singleton对象。

除了用反射,我们还可以用Unsafe类实例化多个单例对象,这种方式和反射的区别在于:Unsafe不需要调用构造函数。因为Unsafe是使用C++进行JVM底层控制。代码如下:

public class Hello
{
    public static void main(String[] args) throws Exception
    {
        Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafeField.setAccessible(true);
        Unsafe unsafeInstance = (Unsafe)theUnsafeField.get(null);
        Singleton instanceA = (Singleton)unsafeInstance.allocateInstance(Singleton.class);
        Singleton instanceB = (Singleton)unsafeInstance.allocateInstance(Singleton.class);
        System.out.println(instanceA.hashCode());
        System.out.println(instanceB.hashCode());
        System.out.println(instanceA == instanceB);
    }
}

程序输出:

460141958
1163157884
false

Process finished with exit code 0

可以发现上面的代码根本没有调用Singleton的构造函数,而是直接生成了两个实例。

其实上面的代码并没有太大意义,只是作为知识点可以加深对反射和单例的理解和印象。