您的位置:首页 > Web前端

用枚举增强单例模式的可靠性 - Effective Java 中文版第二版的读书心得(三)

2009-03-12 14:02 211 查看
我们常用的构造单例模式(Singleton)的方法,一般有2种

1 提供一个静态的公共属性

2 提供一个静态的公共方法

这2个方法,都是采用了私有的构造器来防止外部直接构造实例。 但我们可以用反射的方法,获得多个实例。后面我会给出测试的代码。

从1.5开始,枚举也可以用来获得单例,而且更加可靠。同时又自动提供了一些额外的功能。

先看看测试代码:

import java.lang.reflect.Constructor;
/**
 * 测试Singleton的可靠性。
 * 
 * @author 老紫竹(laozizhu.com)
 */
public class TestSingleton {
  public static void main(String[] args) {
    testSingleton1();
    testSingleton2();
    testSingleton3();
  }
  public static void testSingleton1() {
    try {
      // 测试Singletom1
      // 拿到第一个实例
      TestSingleton1 s1 = TestSingleton1.getInstance();
      // 测试拿到第二个实例
      Class c1 = Class.forName("TestSingleton1");
      Constructor[] cons = c1.getDeclaredConstructors();
      Constructor cc1 = cons[0];
      cc1.setAccessible(true);
      TestSingleton1 s2 = (TestSingleton1) cc1.newInstance(null);
      System.out.println(s1 + "/" + s2);
      System.out.println(s1 == s2);
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }
  public static void testSingleton2() {
    try {
      // 测试Singletom1
      // 拿到第一个实例
      TestSingleton2 s1 = TestSingleton2.getInstance();
      // 测试拿到第二个实例
      Class c1 = Class.forName("TestSingleton2");
      Constructor[] cons = c1.getDeclaredConstructors();
      Constructor cc1 = cons[0];
      cc1.setAccessible(true);
      TestSingleton2 s2 = (TestSingleton2) cc1.newInstance(null);
      System.out.println(s1 + "/" + s2);
      System.out.println(s1 == s2);
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }
  public static void testSingleton3() {
    try {
      // 测试Singletom1
      // 拿到第一个实例
      TestSingleton3 s1 = TestSingleton3.getInstance();
      // 测试拿到第二个实例
      Class c1 = Class.forName("TestSingleton3");
      Constructor[] cons = c1.getDeclaredConstructors();
      Constructor cc1 = cons[0];
      cc1.setAccessible(true);
      TestSingleton3 s2 = (TestSingleton3) cc1.newInstance(null);
      System.out.println(s1 + "/" + s2);
      System.out.println(s1 == s2);
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }
}
/**
 * 一个普通的Singletone实现。
 * 
 * @author 老紫竹(laozizhu.com)
 */
class TestSingleton1 {
  private static final TestSingleton1 INSTANCE = new TestSingleton1();
  public static TestSingleton1 getInstance() {
    return INSTANCE;
  }
  private TestSingleton1() {
  }
}
/**
 * 一个用异常强化了的Singletone实现。
 * 
 * @author 老紫竹(laozizhu.com)
 */
class TestSingleton2 {
  private static final TestSingleton2 INSTANCE = new TestSingleton2();
  public static TestSingleton2 getInstance() {
    return INSTANCE;
  }
  private static boolean initSign;
  private TestSingleton2() {
    if (initSign) {
      throw new RuntimeException("实例只能建造一次");
    }
    initSign = true;
  }
}
/**
 * 枚举实现的Singleton
 * 
 * @author 老紫竹(laozizhu.com)
 */
enum TestSingleton3 {
  INSTANCE;
  public static TestSingleton3 getInstance() {
    return INSTANCE;
  }
}


测试结果

TestSingleton1@c17164/TestSingleton1@1fb8ee3

false

java.lang.reflect.InvocationTargetException

at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)

at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)

at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)

at java.lang.reflect.Constructor.newInstance(Constructor.java:513)

at TestSingleton.testSingleton2(TestSingleton.java:43)

at TestSingleton.main(TestSingleton.java:11)

Caused by: java.lang.RuntimeException: 实例只能建造一次

at TestSingleton2.<init>(TestSingleton.java:103)

... 6 more

java.lang.IllegalArgumentException: Cannot reflectively create enum objects

at java.lang.reflect.Constructor.newInstance(Constructor.java:511)

at TestSingleton.testSingleton3(TestSingleton.java:61)

at TestSingleton.main(TestSingleton.java:12)

小结:可见,只有第三种枚举的方法才是最安全的。

关于里面提到的,在序列化是可能出现的问题,我看以后在讨论吧。不过因为枚举实现的单例没有这个问题,所以我看以后就用枚举好了,何必自己跟自己过不去呢?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐