最近经常使用到了反射技术,发现普通单例模式是存在一定程度上的缺陷,例如:
当普通单例模式遇到反射:
public class T{
public static void main(String[] args) throws Exception {
R r1 = R.getInstance();
Constructor<R> constructor = R.class.getDeclaredConstructor();
constructor.setAccessible(true);
R r2 = constructor.newInstance();
System.out.println("r1"+r1.toString());
System.out.println("r2"+r2.toString());
}
}
class R{
private static R r = new R();
private R(){}
public static R getInstance() {
return r;
}
}
输出结果:
r1com.R@6a5fc7f7
r2com.R@3b6eb2ec
可以看到,已经不是当时的对象
*如何避免这个问题?
public class T{
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
R r1 = R.getInstance();
Constructor<R> constructor = R.class.getDeclaredConstructor();
constructor.setAccessible(true);
R r2 = constructor.newInstance();
System.out.println("r1"+r1.toString());
System.out.println("r2"+r2.toString());
}
}
class R{
//需要在构造函数中对实例化次数进行统计,大于一次就抛出异常。
private static int count = 0;
private static R r = new R();
private R(){
synchronized (R.class) {
if(count > 0){
throw new RuntimeException("创建了两个实例");
}
count++;
}
}
public static R getInstance() {
return r;
}
}
查看运行结果:
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.T.main(T.java:11)
Caused by: java.lang.RuntimeException: 创建了两个实例
at com.dip.pv.R.<init>(T.java:24)
... 5 more
如果创建不了两个对象,那岂不是埋没了反射的名声。。。