Java 单例模式一般有两种实现:
1.暴露静态final字段
- public class Elvis {
- public static final Elvis INSTANCE = new Elvis();
- private Elvis(){;;;}
- public void leaveTheBuilding() {;;;}
- }
2.暴露静态方法
- public class Elvis {
- private static final Elvis INSTANCE = new Elvis();
- private Elvis(){;;;}
- public static Elvis getInstance() { return INSTANCE;}
- public void leaveTheBuilding() {;;;}
- }
如果借助AccessibleObject.setAccessible方法,通过反射机制调用私有构造器,抵御这种方法可以在创建第二次构造器时抛出异常来解决。
自定义异常:
- public class InitializationException extends RuntimeException {
- /**
- *
- */
- private static final long serialVersionUID = 1L;
- public InitializationException(String msg) {
- super(msg);
- }
- }
单例类:
- public class Singleton {
- private static int count = 0;
- private static final Singleton INSTANCE = new Singleton();
- private Singleton() {
- if(count == 1)
- throw new InitializationException("只能初始化一次!");
- count++;
- }
- public static Singleton getInstance() {
- return INSTANCE;
- }
- }
测试类:
- public class Test {
- public static void main(String[] args) throws Exception{
- Singleton s1 = Singleton.getInstance();
- Singleton s2 = Singleton.getInstance();
- System.out.println(s1 == s2); // 返回 true
- // 试图通过反射机制创建实例
- for(Constructor<?> c : s1.getClass().getDeclaredConstructors()) {
- c.setAccessible(true); // AccessibleObject
- Singleton s3 = (Singleton)c.newInstance();
- }
- }
- }
抛出的异常:
- Exception in thread "main" java.lang.reflect.InvocationTargetException
- at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
- at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
- at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
- at java.lang.reflect.Constructor.newInstance(Unknown Source)
- at org.reflect.Test.main(Test.java:17)
- Caused by: org.reflect.InitializationException: 只能初始化一次!
- at org.reflect.Singleton.<init>(Singleton.java:10)
- ... 5 more
JDK1.5及以后,增加了实现Singleton的第三种方法。只需编写一个包含单个元素的枚举类型。
- public enum Adam {
- INSTANCE; //只有一个元素
- public void leaveTheBuilding() {;;;}
- }
使用这种方法的好处是可以防止多次实例化,无偿提供了序列化机制,即使是面对复杂的序列化或者反射公鸡。
当你试图通过反射调用枚举类型的构造器时(默认构造器为private),如果调用了setAccessible(true)方法,将会抛出IllegalArgumentException:
- Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
- at java.lang.reflect.Constructor.newInstance(Unknown Source)
- at org.reflect.Test.main(Test.java:23)
对于前两种单例模式,为了使Singleton能够序列化,除了实现标记接口Serializable外,还需增加类似下面的方法,防止反序列化时生成“假冒”的单例类:
- private Object readResolve() {
- return INSTANCE;
- }
而对于枚举类型,完全不用多此一举。因为枚举类型已经提供了该机制。