面试时候,别只会说饿汉,懒汉了

都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 map=new HashMap();
    
    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接口哦~

The End

你可能感兴趣的:(面试时候,别只会说饿汉,懒汉了)