用枚举增强单例模式的可靠性 - Effective Java 中文版第二版的读书心得(三)
2009-03-12 14:02
211 查看
我们常用的构造单例模式(Singleton)的方法,一般有2种
1 提供一个静态的公共属性
2 提供一个静态的公共方法
这2个方法,都是采用了私有的构造器来防止外部直接构造实例。 但我们可以用反射的方法,获得多个实例。后面我会给出测试的代码。
从1.5开始,枚举也可以用来获得单例,而且更加可靠。同时又自动提供了一些额外的功能。
先看看测试代码:
测试结果
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)
小结:可见,只有第三种枚举的方法才是最安全的。
关于里面提到的,在序列化是可能出现的问题,我看以后在讨论吧。不过因为枚举实现的单例没有这个问题,所以我看以后就用枚举好了,何必自己跟自己过不去呢?
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)
小结:可见,只有第三种枚举的方法才是最安全的。
关于里面提到的,在序列化是可能出现的问题,我看以后在讨论吧。不过因为枚举实现的单例没有这个问题,所以我看以后就用枚举好了,何必自己跟自己过不去呢?
相关文章推荐
- Effective Java 中文版第二版的读书心得(四)通过私有构造器强化不可实例化的能力
- Effective Java 中文版第二版的读书心得(一)
- Effective Java 中文版第二版的读书心得(二)
- JDK1.5新特性:静态导入、可变参数、增强for循环、自动装箱拆箱(享元设计模式)、枚举、注解、泛型、
- 深入浅出设计模式-读书心得2
- Java高新技术1---增强for循环 ---装箱与拆箱享元设计模式--自己写枚举--可变长度的参数
- 深入浅出设计模式-读书心得1
- Java高新技术1---增强for循环 ---装箱与拆箱享元设计模式--自己写枚举--可变长度的参数
- 分析模式读书心得之责任模式
- 【PHP5权威指南】读书心得二——高级面向对象编程和设计模式
- [java]jdk1.5的新特性----可变参数、增强for循环、枚举、单例模式
- 《深入理解计算机系统CSAPP(第二版)》读书心得与全书推荐
- 分析模式读书心得之责任模式
- JAVA进阶之旅(一)——增强for循环,基本数据类型的自动拆箱与装箱,享元设计模式,枚举的概述,枚举的应用,枚举的构造方法,枚举的抽象方法
- JAVA进阶之旅(一)——增强for循环,基本数据类型的自动拆箱与装箱,享元设计模式,枚举的概述,枚举的应用,枚举的构造方法,枚举的抽象方法
- 以muppet为例利用模板方法模式增强异常信息的反馈
- 设计模式心得:观察者模式 (observer pattern)
- 《Effective Java》builder模式创建对象
- Java基础&增强 枚举
- 多例设计模式与枚举