都2019年最后一天了,面试的时候不要还只会说饿汉式、懒汉式,显得你很没竞争力
单例模式特点
- 构造方法私有化
- 通过一个静态方法去获取其实例
- 在全局中对象有且只有一个,多线程中需要考虑到线程安全
- 在反序列化时,不要被重新创建新的对象
面试场景重现
面试官:说下单例模式吧
你:单例模式分为饿汉式、懒汉式、DCL……balabala
面试官:还有其他的写法吗?
你:额,我平时用的比较多的就是这几个(我之前面试都是这样回答的啊,怎么换到现在不行了???what???)
单例模式的几种写法
1、饿汉式
public class Singleton{
// 不管你要不要,我先给你准备好
private static Singleton INSTANCE= new Singleton();
// 构造方法私有化
private Singleton(){}
// 通过一个静态方法去获取其实例
public static Singleton getInstance(){
return INSTANCE;
}
}
很明显上面写的单例,满足了单例模式的两个特点,第一个构造方法私有化,第二通过一个静态方法去获取其实例。饿汉式不管你要不要,我都给你初始化好,有时候我只想在调用的时候才初始化怎么办呢?那就往下面看吧~
2、懒汉式
public class Singleton{
private static Singleton INSTANCE = null;
// 构造方法私有化
private Singleton(){}
// 通过一个静态方法去获取其实例
public static synchronized Singleton getInstance(){
if(null == INSTANCE){
instance = new Singleton();
}
return INSTANCE;
}
}
懒汉式相比饿汉式做了改变,在调用的时候再去进行初始化,且方法上加上了synchronized关键字以示同步,虽然解决了饿汉式的问题,但每次调用getInstance()方法的时候,都会产生同步开销,一两个还好,如果是好多个呢?那还是有必要优化一下的
3、Double Check Lock(DCL)
public class Singleton {
// 加上了volatile关键字(禁止指令重排序)
private static volatile Singleton INSTANCE = null;
// 构造方法私有化
private Singleton(){}
// 通过一个静态方法去获取其实例
public static Singleton getInstance() {
if (null == INSTANCE ) { // Single Checked
synchronized (Singleton.class) {
if (null == INSTANCE ) { // Double checked
INSTANCE = new Singleton();
}
}
}
return INSTANCE ;
}
}
什么是指令重排序?volatile是什么?写太多不想看
4、静态内部类
public class Singleton{
private static class SingletonHolder{
public static Singleton INSTANCE = new Singleton();
}
// 构造方法私有化
private Singleton(){}
// 通过一个静态方法去获取其实例
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
和饿汉式一样,利用类加载机制来保证只创建一个实例,但是通过静态内部类的方式去创建的,只要应用中不使用内部类,JVM就不会去加载这个单例类,也就不会创建单例对象,从而实现懒汉式的延迟加载。也就是说可以把静态内部类单例理解成是饿汉式和懒汉式的结合。但是,它有个致命的弱点,如果你的这个单例需要传context,它就没办法满足你了。
5、枚举
class Resource{
}
public enum SomeThing {
INSTANCE;
private Resource instance;
private SomeThing() {
instance = new Resource();
}
public Resource getInstance() {
return instance;
}
}
说实话,枚举的单例写法,我是没写过。你们呢?不研究了,任性……
6、容器
public class SingletonManager {
private static Map<String,Object> map=new HashMap<String, Object>();
private SingletonManager(){}
public static void registerService(String key,Object instance){
if (!map.containsKey(key)){
map.put(key,instance);
}
}
public static Object getService(String key){
return map.get(key);
}
}
这我也没用过,但面试时候要会说吧,看代码就知道了,是单例的统一管理类,通过一个HashMap容器去装。使用时通过key来获取对应类型的对象,这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行操作。
你以为完了吗?
饿汉式、懒汉式、DCL、静态内部类的单例写法都有可能会出现一个问题,那就是反序列化的时候,会被重新创建对象。那么问题来了,怎么解决呢?
public class Singleton implements Serializable {
……
private Object readResolve() throws ObjectStreamException {
// instead of the object we're on,
// return the class variable INSTANCE
return INSTANCE;
}
}
要想写readResolve()这个方法,请记得让你的单例实现Serializable接口哦~