ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

一文带你修炼地狱级【单例模式】内外功,吊打BAT面试官

2021-11-09 10:34:11  阅读:145  来源: 互联网

标签:BAT 面试官 private 吊打 INSTANCE 实例 static 单例 public


文章目录


原文地址: 文章来源于Java中文社群公众号:单例模式,真不简单

前言

单例模式无论在我们面试,还是日常工作中,都会面对的问题。但很多单例模式的细节,值得我们深入探索一下。

1 什么是单例模式

单例模式是一种非常常用的软件设计模式,它定义是单例对象的类只能允许一个实例存在。
该类负责创建自己的对象,同时确保只有一个对象被创建。一般常用在工具类的实现或创建对象需要消耗资源的业务场景。

单例模式的特点:

  • 类构造器私有
  • 持有自己类的引用
  • 对外提供获取实例的静态方法

我们先用一个简单示例了解一下单例模式的用法。

public class SimpleSingleton {
    //持有自己类的引用
    private static final SimpleSingleton INSTANCE = new SimpleSingleton();

    //私有的构造方法
    private SimpleSingleton() {
    }
    //对外提供获取实例的静态方法
    public static SimpleSingleton getInstance() {
        return INSTANCE;
    }
    
    public static void main(String[] args) {
        System.out.println(SimpleSingleton.getInstance().hashCode());
        System.out.println(SimpleSingleton.getInstance().hashCode());
    }
}

打印结果:

1639705018
1639705018

我们看到两次获取SimpleSingleton实例的hashCode是一样的,说明两次调用获取到的是同一个对象。

可能很多朋友平时工作当中都是这么用的,但我要说这段代码是有问题的,你会相信吗?

不信,我们一起往下看。

2 饿汉和懒汉模式

在介绍单例模式的时候,必须要先介绍它的两种非常著名的实现方式:饿汉模式 和 懒汉模式。

2.1 饿汉模式

实例在初始化的时候就已经建好了,不管你有没有用到,先建好了再说。具体代码如下:

public class SimpleSingleton {
    //持有自己类的引用
    private static final SimpleSingleton INSTANCE = new SimpleSingleton();

    //私有的构造方法
    private SimpleSingleton() {
    }
    //对外提供获取实例的静态方法
    public static SimpleSingleton getInstance() {
        return INSTANCE;
    }
}

饿汉模式,其实还有一个变种:

public class SimpleSingleton {
    //持有自己类的引用
    private static final SimpleSingleton INSTANCE;
    static {
       INSTANCE = new SimpleSingleton();
    }

    //私有的构造方法
    private SimpleSingleton() {
    }
    //对外提供获取实例的静态方法
    public static SimpleSingleton getInstance() {
        return INSTANCE;
    }
}

使用静态代码块的方式实例化INSTANCE对象。

使用饿汉模式的好处是:没有线程安全的问题,但带来的坏处也很明显。

private static final SimpleSingleton INSTANCE = new SimpleSingleton();

一开始就实例化对象了,如果实例化过程非常耗时,并且最后这个对象没有被使用,不是白白造成资源浪费吗?

这个时候你也许会想到,不用提前实例化对象,在真正使用的时候再实例化不就可以了?

这就是我接下来要介绍的:懒汉模式。

2.2 懒汉模式

顾名思义就是实例在用到的时候才去创建,“比较懒”,用的时候才去检查有没有实例,如果有则返回,没有则新建。具体代码如下:

public class SimpleSingleton2 {

    private static SimpleSingleton2 INSTANCE;

    private SimpleSingleton2() {
    }

    public static SimpleSingleton2 getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new SimpleSingleton2();
        }
        return INSTANCE;
    }
}

示例中的INSTANCE对象一开始是空的,在调用getInstance方法才会真正实例化。

但这段代码还是有问题。

2.3 synchronized关键字

上面的代码有什么问题?

假如有多个线程中都调用了getInstance方法,那么都走到 if (INSTANCE == null) 判断时,可能同时成立,因为INSTANCE初始化时默认值是null。这样会导致多个线程中同时创建INSTANCE对象,即INSTANCE对象被创建了多次,违背了只创建一个INSTANCE对象的初衷。

那么,要如何改进呢?

答:最简单的办法就是使用synchronized关键字。

改进后的代码如下:

public class SimpleSingleton3 {
    private static SimpleSingleton3 INSTANCE;

    private SimpleSingleton3() {
    }

    public synchronized static SimpleSingleton3 getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new SimpleSingleton3();
        }
        return INSTANCE;
    }
    
    public static void main(String[] args) {
        System.out.println(SimpleSingleton3.getInstance().hashCode());
        System.out.println(SimpleSingleton3.getInstance().hashCode());
    }
}

在getInstance方法上加synchronized关键字,保证在并发的情况下,只有一个线程能创建INSTANCE对象的实例。

这样总可以了吧?

答:不好意思,还是有问题。

有什么问题?

标签:BAT,面试官,private,吊打,INSTANCE,实例,static,单例,public
来源: https://blog.csdn.net/GhostGuest/article/details/121221400

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有