Skip to content

面试速答(先看这里)

**一句话结论:**想要实现一个单例,首先就是要考虑把构造函数设置成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如何设计一个线程安全的单例?

如何破坏单例 ​

📄 ✅如何破坏单例模式?

打开文档:✅如何破坏单例模式?