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.(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;
}
而对于枚举类型,完全不用多此一举。因为枚举类型已经提供了该机制。