真正的单例模式

  今天看effective java是看到上面对单例模式的介绍说,把构造器设置成私有并不能完全实现单例,因为可以通过反射来调用这个构造器,从而构造出不同的实例,今天我做了一个测试,果真是这样,下面是我的代码。

Singleton.java

  

 1 package com.swjtu.crazykid;

 2 

 3 public class Singleton {

 4     private static Singleton singleton = new Singleton();

 5     private Singleton(){

 6         if(singleton != null)

 7             throw new IllegalAccessError();

 8     }

 9     

10     public static Singleton getInstance(){

11         return singleton;

12     }

13     

14 }

下面是测试类:

 1 package com.swjtu.crazykid.test;

 2 

 3 import java.lang.reflect.Constructor;

 4 import java.lang.reflect.Field;

 5 import java.lang.reflect.InvocationTargetException;

 6 

 7 import org.junit.Test;

 8 

 9 import com.swjtu.crazykid.Singleton;

10 

11 public class MyTest {

12     @Test

13     public void TestSingleton(){

14         Singleton singleton1 = Singleton.getInstance();

15         System.out.println(singleton1.hashCode());

16         Class clazz = Singleton.class;

17         Constructor[] c;

18         try {

19             c = clazz.getDeclaredConstructors();

20             c[0].setAccessible(true);

21             Singleton singleton = (Singleton) c[0].newInstance();

22             System.out.println(singleton.hashCode());

23         }  catch (SecurityException e) {

24             // TODO Auto-generated catch block

25             e.printStackTrace();

26         } catch (InstantiationException e) {

27             // TODO Auto-generated catch block

28             e.printStackTrace();

29         } catch (IllegalAccessException e) {

30             // TODO Auto-generated catch block

31             e.printStackTrace();

32         } catch (IllegalArgumentException e) {

33             // TODO Auto-generated catch block

34             e.printStackTrace();

35         } catch (InvocationTargetException e) {

36             // TODO Auto-generated catch block

37             e.printStackTrace();

38         }

39     }

40 }

如果把singleton.java的第六和第7行注释掉,会发现测试类的第15行和第22行的输出的hashcode是不一样的。所以这样做并不能做到真正的单例。当我们把singleton中的构造器做些修改,就像上面的代码一样,当里面的实例不是空的时候,调用构造器的时候抛出异常,就能解决了。

  但是,同样的通过反射将这个实例设为空,你会发现照样能构造出两个不同的singleton实例。所以这样也不能真正的做到单例,就是说这样设计的单例无法抵御反射这类的攻击。书上说完美的做法是用enum。

 

你可能感兴趣的:(单例模式)