单例模式哪些不得不说的场景

点击上方「蓝字」关注我们

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乐园

评论
