主题
面试速答(先看这里)
**一句话结论:**想要实现一个单例,首先就是要考虑把构造函数设置成private的,否则的话就可以随时通过构造函数创建对象了,就不是单例了。
60秒标准回答:
想要实现一个单例,首先就是要考虑把构造函数设置成private的,否则的话就可以随时通过构造函数创建对象了,就不是单例了
那把构造函数private之后,就还需要提供一个方法,可以初始化单例对象,并且要保证只能初始化一个单例对象。并且需要考虑线程安全的问题
具体到写法上,主要有5种。分别是懒汉、饿汉、静态内部类、双重校验锁以及枚举
**答题顺序:**结论 → 原理/机制 → 关键流程 → 场景与取舍 → 易错点
回答主线:
- **要点1:**那把构造函数private之后,就还需要提供一个方法,可以初始化单例对象,并且要保证只能初始化一个单例对象。
- **要点2:**具体到写法上,主要有5种。
- **要点3:**所谓懒汉,就是在需要的时候才会去创建对象。
- **要点4:**好处就是避免提前创建浪费资源,但是缺点也明显,就是第一创建的时候浪费时间。
- **要点5:**所谓饿汉,就是在类刚一初始化的时候就立即把单例对象创建出来。
**记忆锚点:**private → 通过构造函数创建对象 → 能初始化一个单例对象 → 以上两种都是饿汉模式 → 静态内部类 → 双重校验锁
关键取舍:
- 好处就是避免提前创建浪费资源,但是缺点也明显,就是第一创建的时候浪费时间。
易错提醒:
- 好处就是避免提前创建浪费资源,但是缺点也明显,就是第一创建的时候浪费时间。
追问准备:
- 围绕「private」:底层原理是什么?使用时有哪些边界和常见坑?
- 围绕「通过构造函数创建对象」:底层原理是什么?使用时有哪些边界和常见坑?
- 围绕「能初始化一个单例对象」:底层原理是什么?使用时有哪些边界和常见坑?
- 如果线上出现异常,你会如何定位、验证并规避?
典型回答
想要实现一个单例,首先就是要考虑把构造函数设置成private的,否则的话就可以随时通过构造函数创建对象了,就不是单例了。
那把构造函数private之后,就还需要提供一个方法,可以初始化单例对象,并且要保证只能初始化一个单例对象。并且需要考虑线程安全的问题。
具体到写法上,主要有5种。分别是懒汉、饿汉、静态内部类、双重校验锁以及枚举。
扩展知识
懒汉
所谓懒汉,就是在需要的时候才会去创建对象。
plain
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}好处就是避免提前创建浪费资源,但是缺点也明显,就是第一创建的时候浪费时间。
饿汉
所谓饿汉,就是在类刚一初始化的时候就立即把单例对象创建出来。
plain
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}plain
public class Singleton {
private Singleton instance = null;
static {
instance = new Singleton();
}
private Singleton (){}
public static Singleton getInstance() {
return this.instance;
}
}以上两种都是饿汉模式的具体实现。
静态内部类
plain
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}枚举
plain
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}双重校验锁
plain
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}📄 ✅有了synchronized为什么还需要volatile?
打开文档:✅有了synchronized为什么还需要volatile?
扩展知识
CAS实现单例
📄 ✅不使用synchronized和Lock如何设计一个线程安全的单例?
打开文档:✅不使用synchronized和Lock如何设计一个线程安全的单例?