首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >单例模式哪些不得不说的场景

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

作者头像
BUG弄潮儿
发布2020-12-02 10:31:53
发布2020-12-02 10:31:53
3570
举报
文章被收录于专栏:JAVA乐园JAVA乐园
0x01:单例模式被破坏
  • 反射技术非常强大,可以通过setAccessible()来修改构造器,字段,方法的可见性。单例模式的构造方法是私有的,如果将其可见性设为public,那么将无法控制对象的创建。
代码语言:javascript
复制
public class Singleton {

private static Singleton instance = new Singleton();   

    private Singleton() {} 

    public static Singleton getInstance() {
        return instance;
    }
}

通过以下代码,即可构建两个不同的对象

代码语言:javascript
复制
public class SingletonCmd {

 public static void main(String[] args) throws Exception{
        Singleton singleton = Singleton.getInstance();
        Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        Singleton newSingleton  = constructor.newInstance();

        System.out.println(singleton.hashCode());
        System.out.println(newSingleton.hashCode());

    }

}

另外,克隆、序列化都可能导致单例模式被破坏。

0x02:防止反射破坏单例模式

主要通过在单例对象里添加一个标识位,在创建了一个时,改变该标识位。

代码语言:javascript
复制
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<Singleton> c1 = Singleton.class;
      Constructor<Singleton> constructor = c1.getDeclaredConstructor();
      constructor.setAccessible(true);
      Singleton s2 = constructor.newInstance();
      System.out.println(s1 == s2);

  }

}

这种实现并不是非常严谨,因为既然可以通过反射来获取构造函数来创建实例了,那么同样可以通过反射来获取到定义的flag,那么在利用反射调用构造函数之前,先获取到这个flag,将它值重置,那么再次调用构造函数就不会受到限制了,那这样实际上就没有起到防止重复创建对象的效果。这个另外一个实现方案

代码语言:javascript
复制
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,这样外面的其他类就获取不到这个私有的内部类,也就不能改变它的值了,从而保护了单例的实现。

代码语言:javascript
复制
参考:https://blog.csdn.net/shengfengwuying/article/details/89919386
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-11-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 BUG弄潮儿 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档