重温经典之单例模式杂谈篇

177 阅读5分钟

前言

​ 其实写这篇的目的就是随便写一个案例用一个简单的案例让人在不同角度有一种不一样的理解,而不是有的人一直玩命的扣字眼半天转不过来圈或者不理解什么时候用之类的。

吐槽大会之设计模式

甭管是新人还是老手,面对设计模式这四个大字谁也别想跑。

什么什么2020年了你还不会设计模式吗,什么什么熟练 了解常用的设计模式优先。

然后看的文章 帖子写的都是抄书式的(基本上和揭秘某音主播带货那都是一个套路的):

  1. 什么是单例。
    1. 单例设计模式是.....
  2. 什么时候用单例模式。
    1. 保证类只有一个实例
    2. 控制系统资源
    3. .....
  3. 单例设计模式几种写法。
    1. .....

然而看完了会抄了,但是这时候还有人问了我刚看了单例设计模式 说是保证类只有一个实例啥时候用单例啊巴拉巴拉但是这都解决不了我这种菜鸟的疑问,我也不知道写文章的写完了自己明白不明白反正我是不明白(明明白白谁的心?)最后就是面向面试背诵设计模式

至于设计模式 我个人认为写的很好的比较全面的可以看一下

戳这里 秉心说TM - 钻钻 “单例” 的牛角尖

走进今日案例:

  1. C/S 本地化显示图片

    需求:用一个弹框实时展示不同图片时 你使用单例写的代码是这样的话

    public class SwingShowImg extends JFrame {
    
        private static final long serialVersionUID = 1L;
    
        private static SwingShowImg instance;
    
        JLabel label;
    
        private SwingShowImg (){}
    
        public static synchronized SwingShowImg getInstance() {
            if (instance == null) {
                instance = new SwingShowImg();
            }
            return instance;
        }
    
        public void showImage(String title, String url){
            try {
                if(null != label){
                    remove(label);
                }
                setTitle(title);
                setSize(1366, 768);
                setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                label = new JLabel();
                Image image = new ImageIcon(ImageIO.read(new File(url))).getImage();
                Icon icon = new ImageIcon(image);
                label.setIcon(icon);
                add(label);
                setVisible(true);
            } catch (Exception e) {
                System.out.println("初始化失败" + e.getMessage());
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            SwingShowImg swingShowImg = SwingShowImg.getInstance();
            swingShowImg.showImage("Test2","D:/17.jpg");
            swingShowImg.showImage("Test2","D:/16.jpg");
        }
    
    }
    

    效果图:

    while(true){

    }

    ​ 如果不使用单例无脑new对象显示的话就会出现左边的问题出现多个弹框,而右边使用单例的一直使用的是同一个弹框效果正常。

    ​ 那机智的小伙伴就说了我不用单例我按照下面这么写代码不就完了吗,答案是可以的,但是使用new的方式的不好之处在于,如果我们的需求是使用同一个View展示数据,你自己在开发的时候可以保证使用的是同一个对象,但是你突然带了一个徒弟,徒弟不管三七二十一一把梭可能又会搞出一个new对象,又回到上图左面的情况,所以使用单例我们可以保证是唯一的(这里不考虑反射等情况)。

    SwingShowImg swingShowImg = new SwingShowImg();
    for(;;){
        swingShowImg.show()
    }
    

单例模式和static

还有的小伙伴想到可以以静态代码块的方式实例化

private static Test test;

static {
    test  = (Test) ApplicationContextUtil.getBean(Test.class);
}
  • static块初始化:

​ 静态初始化的时候只在Class对象第一次加载的时候进行,也就是我们常说的static只会被加载一次,其中深入理解static为什么只被加载一次如下:

在掘金看张师傅的JVM小册时类的初始化简化步骤是这么写道:

  1. 类加载校验,将类加载到虚拟机
  2. 执行static代码块
  3. 为对象分配堆对象
  4. 对成员变量进行初始化...
  5. 调用初始化代码块...

由上我们知道了static代码块是在类加载时初始化阶段完成的,我们知道了类的加载流程(一个类加载器实际上只可以加载一次类)后其实就可以理解为什么static只记载一次了。

  • 单例模式初始化:

      private SwingShowImg (){}
    
      public static synchronized SwingShowImg getInstance() {
          if (instance == null) {
              instance = new SwingShowImg();
          }
          return instance;
      }
    

通过如上代码我们不难看出, 几个关键字 static synchronized new

static方法并不会像static块那样被直接初始化,synchronized锁证明单例模式本身是非线程安全的 但我们可以通过加锁 双重检查锁等保证,new更印证了不会像static块那样被初始化。

  • 简单总结

    其实我个人认为各有千秋,单例的好处在于保证类在任意地方都是唯一的,需要考虑懒汉 饿汉 线程安全,而static块初始化原生就是线程安全的,默认就加载占用内存,但是可以在任意的地方定义易混乱,但简单方便。

总结

我个人理解是:我们了解设计模式主要是为了更好的学习其思想,真正理解其思想和优缺点后在熟悉业务的情况下可能才会更多的应用,而在学习思想的路上可能会有一些不惑,但是我们可以阅读更多不同人书写的文章从不同角度理解一个事情可能自己就会有一些自己的理解,毕竟每个人的理解的程度和理解感悟也不同给每个人启发也是不同,学习是书越读越厚,越研究头皮越凉。