面试官系统之设计模式(单例模式)

单例模式:

顾名思义就是只能有一个,不能在出现第二个。就如同地球上没有两片完全一模一样的树叶一样。

程序猿的角度理解:一个类有且只能有一个实例,不能出现第二个,并且整个项目系统中都能访问该实例。

面试官:为啥不能出现第二个?

程序猿:。。。(这个面试官是笨蛋,出现第二个那就不叫单例模式了,那至少得叫双例模式,是吧?)

面试官:为啥整个项目系统中都能访问该实例?

程序猿:不能访问该实例,那new出来还有什么意义。

面试官:那你给我写个单例模式吧

程序猿:写就写,这能难倒我,哼,先写个懒汉式单例(第一种

package com.tang.study.singleton;

/**
 * 单例模式
 * 懒汉式单例
 * Created by tgp on 2018/4/15.
 */
public class SingletonPatternOne {

    //关键点0:构造函数是私有的
    private SingletonPatternOne(){}

    ///关键点1:声明单例对象是静态的
    private static SingletonPatternOne instance  = null;

    //通过静态方法来构造对象
    public static SingletonPatternOne getInstance(){
        if(instance == null){//关键点2:判断单例对象是否已经被构造
            instance = new SingletonPatternOne();
        }
        return instance;
    }

    public void showMessage(){
        System.out.println("懒汉式单例:啥时候用啥时候NEW。");
    }

}

面试官:你这代码遇到多线程的并发条件下就不安全了吧。在if判断那里,如果两个线程同时访问,有可能给new出多个单例实例,都多个了,还是屁的“单例”啊。

程序猿:(额,好像也是,幸亏我还有点干货,再撸一段),那我给您再写一段(第二种

/**
 * 单例模式
 * 饿汉式单例
 * Created by tgp on 2018/4/15.
 */
public class SingletonPatternTwo {

    //关键点0:构造函数是私有的
    private SingletonPatternTwo(){
    }

    ///关键点1:声明单例对象是静态的
    private static SingletonPatternTwo instance  = new SingletonPatternTwo();

    //通过静态方法来构造对象
    public static SingletonPatternTwo getInstance(){
        return  instance;
    }

    public void showMessage(){
        System.out.println("饿汉式单例:管你用不用,我都先给你new出来,用不用是你的事,一劳永逸,省的你每次用时都来烦我。");
    }
面试官:那你给我讲讲你写的这种单例模式的优缺点

程序猿:

   优点:这种写法在类加载的时候就完成对象的实例化,避免了线程不安全的问题。

   缺点:在类加载的时候就完成了实例化,如果这个类一直没用,就会造成内存的浪费。

面试官:这种方式是解决了线程不安全的问题,但内存浪费总是不太好吧,尤其是服务器的内存都是很贵的

程序猿:好吧,那我给您优化优化第三种

public class SingletonPatternThree {

    //关键点0:构造函数是私有的
    private SingletonPatternThree(){}

    ///关键点1:声明单例对象是静态的
    private static SingletonPatternThree instance  = null;

    //通过静态方法来构造对象
    public static synchronized SingletonPatternThree getInstance(){
        if(instance == null){//关键点2:判断单例对象是否已经被构造
            instance = new SingletonPatternThree();
        }
        return instance;
    }

    public void showMessage(){
        System.out.println("懒汉式加锁:第一次调用才初始化,避免内存浪费,同时线程安全");
    }
}
面试官:内存浪费和线程安全的问题都解决了,那方法加锁会影响效率,多线程的情况,第一个访问方法期间,其它用户只能等待,用户体验太差

程序猿:(有完没完了。。)逼我放大招第四种:双重检查)

 //通过静态方法来构造对象
    public static SingletonPatternFour getInstance(){
        if(instance == null){//关键点2:判断单例对象是否已经被构造
            synchronized(SingletonPatternFour.class){//关键点3:加锁
                if(instance == null){//关键点4:二次判断单例是否已经被构造
                    instance = new SingletonPatternFour();
                }
            }
        }
        return instance;
    }

    public void showMessage(){
        System.out.println("进行了两次if (instance == null)检查,这样就可以保证线程安全了。" +
                "这样,实例化代码只用执行一次,后面再次访问时,判断if (instance == null),直接return实例化对象。");
    }

面试官:这种不错,那你还会其它的实现方法吗?

程序猿:(我会的可多了,都拿出来怕吓着你)我还会几种别的第五种:静态内部类

public class Singleton {

    private Singleton() {}

    private static class SingletonInstance {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonInstance.INSTANCE;
    }
}
程序猿:类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。

优点:避免了线程不安全,延迟加载,效率高。

第六种:枚举

public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
} 
经验之谈: 一般情况下,推荐双检锁方式和静态内部类。

你可能感兴趣的:(java,设计模式)