单例模式哪些不得不说的场景
点击上方「蓝字」关注我们
0x01:单例模式被破坏
反射技术非常强大,可以通过
setAccessible()
来修改构造器,字段,方法的可见性。单例模式的构造方法是私有的,如果将其可见性设为public
,那么将无法控制对象的创建。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
通过以下代码,即可构建两个不同的对象
public class SingletonCmd {
public static void main(String[] args) throws Exception{
Singleton singleton = Singleton.getInstance();
Constructor constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton newSingleton = constructor.newInstance();
System.out.println(singleton.hashCode());
System.out.println(newSingleton.hashCode());
}
}
另外,克隆、序列化都可能导致单例模式被破坏。
0x02:防止反射破坏单例模式
主要通过在单例对象里添加一个标识位,在创建了一个时,改变该标识位。
public class Singleton {
private static Boolean flag = Boolean.FALSE;
private static Singleton singleton = null;
private Singleton() {
if (!flag) {
synchronized (Singleton.class) {
if(!flag){
flag = true;
}else{
throw new RuntimeException("正在破坏单例模式");
}
}
} else {
throw new RuntimeException("正在破坏单例模式");
}
}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
public static void main(String[] args) throws Exception {
Singleton s1 = Singleton.getInstance();
Class c1 = Singleton.class;
Constructor constructor = c1.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton s2 = constructor.newInstance();
System.out.println(s1 == s2);
}
}
这种实现并不是非常严谨,因为既然可以通过反射来获取构造函数来创建实例了,那么同样可以通过反射来获取到定义的flag,那么在利用反射调用构造函数之前,先获取到这个flag,将它值重置,那么再次调用构造函数就不会受到限制了,那这样实际上就没有起到防止重复创建对象的效果。这个另外一个实现方案
public class Singleton {
private static ImmutableBoolean flag = new ImmutableBoolean();
private static Singleton singleton = null;
private Singleton() {
if (flag.getCount() <= 0) {
synchronized (Singleton.class) {
if (flag.getCount() <= 0) {
flag.setCount();
} else {
throw new RuntimeException("正在破坏单例模式1");
}
}
} else {
throw new RuntimeException("正在破坏单例模式2");
}
}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
private static class ImmutableBoolean {
private static int count = 0;
public ImmutableBoolean() {
}
public void setCount() {
synchronized (Singleton.class) {
if (count <= 0) {
count++;
} else {
throw new RuntimeException("counterror");
}
}
}
public int getCount() {
return count;
}
}
}
定义一个私有的内部类,然后用这个内部类的属性来作为flag,这样外面的其他类就获取不到这个私有的内部类,也就不能改变它的值了,从而保护了单例的实现。
参考:https://blog.csdn.net/shengfengwuying/article/details/89919386
扫码二维码
获取更多精彩
Java乐园
评论