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

共 2244字,需浏览 5分钟

 ·

2020-11-19 21:26

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

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

有用!分享+在看☟


浏览 18
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报