JavaSE面试题之单例模式

Posted JadeXu07

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaSE面试题之单例模式相关的知识,希望对你有一定的参考价值。

一、单例模式介绍

单例,即单个实例,即某个类在整个系统中只能有一个实例对象可被获取和使用。

例如:代表JVM运行环境的Runtime类

所谓单例,那就得:

1、某个类只能有一个实例,即不能被外界new出来。所以构造器需私有

2、这个类必须自行创建这单个实例

3、这个类需要自行向外界提供这个实例,所以需要有个静态变量或静态方法去调用

二、单例模式的几种常见实现形式

总结有两大种:
	饿汉式:直接创建对象,不存在线程安全问题
	-	直接实例化(简洁直观)
	-	枚举式(最简洁,JDK1.5才开始有枚举)
	-	静态代码块(适合初始化时需要传参的类)
	
	懒汉式:只有调用对象才会创建对象,即延迟创建对象
	-	线程不安全(适合单线程)
	-	线程安全(适合多线程)
	-	静态内部类(适合多线程)

1、饿汉式

1. 直接实例化

/**
 * @author jadexu
 * 饿汉式1:直接实例化
 * 1、构造器私有化
 * 2、类加载时就创建
 * 3、向外提供这个实例
 * 4、用final修饰体现单例不能被修改
 *
 * 适合简单直接,初始化时不需要有参构造的场景
 */
public class Singleton1 {
    /**
     * 公共的,静态,final
     */
    public final static Singleton1 INSTANCE = new Singleton1();

    /**
     * 构造函数私有化
     */
    private Singleton1(){

    }
}

2. 枚举式

/**
 * @author jadexu
 * 饿汉式2:枚举
 * JDK1.5开始出现枚举类型,枚举表示某类型的有限个对象
 * 枚举默认不能实例化,变量是final修饰的
 * 所以又不能构造,又不能修改
 * 那么限定为一个,就相当于单例了
 */
public enum Singleton2 {
    INSTANCE
}

测试

    @Test
    public void singleton2Test(){
        /**
         * 报错,枚举类型不能被实例化
         */
//        Singleton2 singleton2 = new Singleton2();

        /**
         * 报错,枚举类型是final的,不能被修改
         */
//        Singleton2.INSTANCE = null;

        /**
         * 只能直接调用唯一实例
         */
        Singleton2 singleton2 = Singleton2.INSTANCE;
    }

3. 静态内部块

/**
 * @author jadexu
 * 饿汉式3:静态代码块
 * 和直接实例化类似
 * 就是会在静态代码块里创建实例
 *
 * 适合初始化时需要有参构造的场景
 */
public class Singleton3 {
    public final static Singleton3 INSTANCE;

    static {
        //假装这是初始化时需要有参构造
        String info = "test";
        INSTANCE = new Singleton3(info);
    }

    private Singleton3(String info){

    }
}

2、懒汉式

1. 线程不安全

/**
 * @author jadexu
 * 懒汉式1:线程不安全
 * 1、构造器私有化
 * 2、用一个私有的静态变量保存唯一实例
 * 3、提供一个静态方法,获得这个实例
 *
 * 适合单线程
 */
public class Singleton4 {
    private static Singleton4 instance;

    private Singleton4(){

    }

    public static Singleton4 getInstance() throws InterruptedException {
        //判断静态变量是否为空,如果为null就创建一个,不为null就直接返回
        if (instance == null){

            instance = new Singleton4();
        }
        return instance;
    }
}

测试

    @Test
    public void singleton4Test() throws ExecutionException, InterruptedException {
        Callable<Singleton4> callable = new Callable<Singleton4>() {
            @Override
            public Singleton4 call() throws Exception {
                return Singleton4.getInstance();
            }
        };

        ExecutorService service = Executors.newFixedThreadPool(2);
        Future<Singleton4> f1 = service.submit(callable);
        Future<Singleton4> f2 = service.submit(callable);

        Singleton4 s1 = f1.get();
        Singleton4 s2 = f2.get();

        System.out.println(s1 == s2);

        service.shutdown();

    }

2. 线程安全

/**
 * @author jadexu
 * 懒汉式2:线程安全(双检锁)
 * 适合多线程场景,但是效率低
 */
public class Singleton5 {
    private static volatile Singleton5 instance;

    public static Singleton5 getInstance(){

        if (instance == null){

            synchronized (Singleton5.class){
                if (instance == null){
                    instance = new Singleton5();
                }
            }

        }

        return instance;
    }

}

测试

	@Test
    public void singleton5Test() throws ExecutionException, InterruptedException {
        Callable<Singleton5> callable = new Callable<Singleton5>() {
            @Override
            public Singleton5 call() throws Exception {
                return Singleton5.getInstance();
            }
        };

        ExecutorService service = Executors.newFixedThreadPool(2);
        Future<Singleton5> f1 = service.submit(callable);
        Future<Singleton5> f2 = service.submit(callable);

        Singleton5 s1 = f1.get();
        Singleton5 s2 = f2.get();

        System.out.println(s1 == s2);

        service.shutdown();
    }

3. 静态内部类

/**
 * @author jadexu
 * 懒汉式3:静态内部类
 * 1、构造器私有
 * 2、私有静态内部类
 * 3、提供一个公共的静态方法
 *
 * 静态内部类不会随着外部类的加载而加载
 * 当调用公共静态方法getInstance时,才会加载Inner静态内部类
 * 然后创建实例
 * 因为是在内部类加载和初始化时创建的,因此是线程安全的
 *
 * 适合于多线程的场景
 */
public class Singleton6 {

    private Singleton6(){

    }

    private static class Inner{
        private static final Singleton6 INSTANCE = new Singleton6();
    }

    public static Singleton6 getInstance(){
        return Inner.INSTANCE;
    }
}

以上是关于JavaSE面试题之单例模式的主要内容,如果未能解决你的问题,请参考以下文章

Java 面试之单例模式

设计模式之单例模式

面试设计模式系列之单例模式

面试设计模式系列之单例模式

面试设计模式系列之单例模式

面试设计模式系列之单例模式