创建和销毁对象-用私有构造器或者枚举类强化Singleton属性

358 阅读2分钟

这是我参与8月更文挑战的第12天,活动详情查看:8月更文挑战

什么是单例(Singleton)

单例模式(Singleton)的目的是为了保证在一个进程中,某个类有且仅有一个实例。 因为这个类只有一个实例,因此,自然不能让调用方使用构造器来创建实例了。一般都用instance来表示,一看这个就知道这是个单例了。

单例为什么那么常问

是因为这个题目可以问到很多知识点。比如线程安全、类加载机制、synchronized 的原理、volatile 的原理、指令重排与内存屏障、枚举的实现、反射与单例模式、序列化如何破坏单例、CAS、CAS 的 ABA 问题、Threadlocal 等知识。一般情况下,只需要从单例开始问起,大概就可以完成一场面试的整个流程,把想问的东西都问完,可以比较全面的了解一个面试者的水平。

不优雅的方式

在1.5之前,实现Singleton有两种方法,本质都是一致的,将构造器保持为私有,并导出公有的静态成员,以便客户端可以访问该类的唯一实例。

公有静态final成员

public class Singleton{
    public static final Singleton instance = new Singleton();
    private Singleton(){
    ......
    }
}

这种方式的缺点是可以通过反射来调用私有构造器再创建,解决办法就是在构造器中进行检查,防止二次创建。 这种有点类似静态内部类实现单例

public class Singleton {
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton() {
    }
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

静态内部类创建单例的优点是外部类加载时并不需要立即加载静态内部类,静态内部类不被加载就不会初始化INSTANCE ,所以就不会占用内存。只有getInstance第一次被调用之后才会去初始化INSTANCE ,既保证了线程安全,也保证了单例的唯一性,也延迟了单例的实例化。

静态工厂方法

class Singleton{
    private static final Singleton instance = new Singleton();
    public static Singleton getInstance(){
        return instance;
    }
    private Singleton(){};
}

优雅的方式

单元素枚举类型实现单例

public enum User{
    INSTANCE;
    public void leave() { ... }
}

从1.5开始引入的枚举类,帮助我们可以简洁的实现单例,甚至可以防范反射攻击,防止多次实例化!它已经成为实现Singleton的最佳方式。

具体使用

使用注解

日常工作中我们的服务类大部分都是想要唯一实例的,那么我们都要这么写么?
不,有了Spring的帮助,我们直接使用对应的注解比如@Component就可以一步实现单例,当我们想要使用这个类的时候直接通过@autowire修饰的成员变量就可以直接获得这个类的实例了,也可以使用getBean来获取这个类。