除了枚举单例,其余都能被反射破坏。
双重检查+懒汉式+私有构造判断(这已经是比较安全的写法,网上说不能破坏):
package com.yuanping.sjms_demo.singleton;
/**
* 单例模式标准,私有静态成员变量,私有构造方法,公共静态获取实例的方法
*/
public class DoubleCheck {
private static volatile DoubleCheck doubleCheck = null; //volitile防止指令重排序
private static int count = 0;
private DoubleCheck(){
synchronized (DoubleCheck.class) {
if(count > 0){
throw new RuntimeException("创建了两个实例");
}
count++;
}
}
public static DoubleCheck getInstance(){
if (doubleCheck == null){
synchronized (DoubleCheck.class){
if (doubleCheck == null){
doubleCheck = new DoubleCheck();
}
}
}
return doubleCheck;
}
}
有同学说加这个可以防止反射破坏。看如下Test 是怎么破坏的
public class Tests {
public static void main(String[] args) {
try {
Class simple = Class.forName("com.yuanping.sjms_demo.singleton.DoubleCheck");
Constructor declaredConstructor = simple.getDeclaredConstructor(null);//获取定义构造方法
declaredConstructor.setAccessible(true);//设置私有方法可以访问
Field count = simple.getDeclaredField("count"); //获取成员变量
count.setAccessible(true); //设置私有变量可以访问
count.set(simple,-100);//重新设置属性
Object o = (Integer)count.get(simple);//获取属性
System.out.println(o);
Object o1 = declaredConstructor.newInstance();
Object o2 = declaredConstructor.newInstance();
System.out.println(o1+"|o2"+o2);
} catch (ClassNotFoundException|NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
}
照样两个对象
所以这时候就只能用枚举了,因为反射的newInstants方法中限制了对枚举的实例化。所以看到的错误日志其实这在以下打出的。
写个枚举类和测试类测一下:
/**
* 枚举测试类
*/
public enum Etp {
INSTANCE;
public Etp getInstance(){
return INSTANCE;
}
}
测试类
public class Test {
public static void main(String[] args) {
Etp instance = Etp.INSTANCE;
try {
Class simple = Class.forName("com.yuanping.sjms_demo.singleton.Etp");
Constructor declaredConstructor = simple.getDeclaredConstructor(String.class, int.class);
declaredConstructor.setAccessible(true);
Etp o = (Etp)declaredConstructor.newInstance();
System.out.println(instance == o);
} catch (ClassNotFoundException e) {
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
出错的行数和打印的日志刚好对应newInstants中的异常 ,所以枚举才是唯一单例。