面试题:写一个你认为最好的单例模式
面试考察点
考察目的: 单例模式可以考察非常多的基础知识,因此对于这种问题,很多面试官都会问。 小伙伴要注意,在面试过程中,但凡能够从多个维度考察求职者能力的题目,一定不会被抛弃,特别是比较泛的问题,比如: ”请你说说对xxx的理解“之类。
考察范围: 工作1到5年经验,随着经验的提升,对于该问题的考察深度越深。
好友添加:huany6880 加入资深Java学习交流圈,并有系统整理了一套java初学/进阶者最佳的学习方法以及路 线图大纲,Java各核心知识点、架构主流技术资料/源码以及最新大厂面试题定期更新!
背景知识
单例模式,是一种软件设计模式,属于创建型模式的一种。
它的特性是:保证一个类只有唯一的一个实例,并提供一个全局的访问点。
基于这个特性可以知道,单例模式的好处是,可以避免对象的频繁创建对于内存的消耗,因为它限制了实例的创建,总的来说,它有以下好处:
-
控制资源的使用,通过线程同步来控制资源的并发访问;
-
控制实例产生的数量,达到节约资源的目的。
-
作为通信媒介使用,也就是数据共享,它可以在不建立直接关联的条件下,让多个不相关的两个线程或者进程之间实现通信。
在实际应用中,单例模式使用最多的就是在Spring的IOC容器中,对于Bean的管理,默认都是单例。一个bean只会创建一个对象,存在内置map中,之后无论获取多少次该bean,都返回同一个对象。
下面来了解单例模式的设计。
单例模式设计
既然要保证一个类在运行期间只有一个实例,那必然不能使用new
关键字来进行实例。
所以,第一步一定是私有化该类的构造方法,这样就防止了调用方自己创建该类的实例。
接着,由于外部无法实例化该对象,因此必须从内部实例化之后,提供一个全局的访问入口,来获取该类的全局唯一实例,因此我们可以在类的内部定义一个静态变量来引用唯一的实例,作为对外提供的实例访问对象。基于这些点,我们可以得到如下设计。
public class Singleton {
// 静态字段引用唯一实例:
private static final Singleton INSTANCE = new Singleton();
// private构造方法保证外部无法实例化:
private Singleton() {
}
}
接着,还需要给外部一个访问该对象实例INSTANCE
的方法,我们可以提供一个静态方法
public class Singleton {
// 静态字段引用唯一实例:
private static final Singleton INSTANCE = new Singleton();
// 通过静态方法返回实例:
public static Singleton getInstance() {
return INSTANCE;
}
// private构造方法保证外部无法实例化:
private Singleton() {
}
}
这样就完成了单例模式的设计,总结来看,单例模式分三步骤。
- 使用
private
私有化构造方法,确保外部无法实例化; - 通过
private static
变量持有唯一实例,保证全局唯一性; - 通过
public static
方法返回此唯一实例,使外部调用方能获取到实例。
单例模式的其他实现#
既然单例模式只需要保证程序运行期间只会产生唯一的实例,那意味着单例模式还有更多的实现方法。
- 懒汉式单例模式
- 饿汉式单例模式
- DCL双重检查式单例
- 静态内部类
- 枚举单例
- 基于容器实现单例
懒汉式单例模式
懒汉式,表示不提前创建对象实例,而是在需要的时候再创建,代码如下。
public class Singleton {
private static Singleton instance;
private Singleton() {
}
// synchronized方法,多线程情况下保证单例对象唯一
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
其中,对getInstance()
方法,增加了synchronized
同步关键字,目的是为了避免在多线程环境下同一时刻调用该方法导致出现多实例问题(线程的并行执行特性带来的线程安全性问题)。
优点: 只有在使用时才会实例化单例,一定程度上节约了内存资源。
缺点: 第一次加载时要立即实例化,反应稍慢。每次调用getInstance()方法都会进行同步,这样会消耗不必要的资源。这种模式一般不建议使用。
DCL双重检查式单例
DCL双重检查式单例模式,是基于饿汉式单例模式的性能优化版本。
/**
* DCL实现单例模式
*/
public class Singleton {
private static volatile Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
// 两层判空,第一层是为了避免不必要的同步
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {// 第二层是为了在null的情况下创建实例
instance = new Singleton();
}
}
}
return instance;
}
}
从