写单例模式,有五六种写法,但总结下来就三步
- 构造方法私有化
- 持有自身的静态引用
- 提供获取自身引用的静态方法
对于构造方法私有化,目的就是为了防止在这个类的外部,被人显示的使用new关键字来进行对象的实例化,一个方法如果变成思私有的,那在这个类外部就没办法调用了。
持有自身的静态引用,那就是为了有朝一日我们能够创造一个唯一一个实例,然后把这个实例的唯一一个引用提供出来,这里非得用静态的原因,是因为我们提供获取这个引用的方法必须是静态的也就是第三点,而静态的方法里面用的东西必须是静态的所以我们持有的自身引用也必须是静态的。
而提供获取自身引用的静态方法,是因为我们没法显示的new一个实例出来了,那总得有获取这个唯一实例的方法嘛,不然怎么提供给第三方使用呢?
第一种写法
public class Singleton01 {
//1. 构造方法私有化
private Singleton01() {
}
//2. 持有自身的静态引用,饿汉式初始化
// 这个地用静态的原因,是因为提供获取实例的方法是静态的,静态方法中用到的方法和变量必须是静态的
// 利用JVM的类加载机制,确保类中的静态变量只在类加载到内存中的时候执行一次的特性
private static final Singleton01 SINGLETON = new Singleton01();
//3. 提供获取的静态方法
public static Singleton01 getInstance() {
return SINGLETON;
}
}
上面这种写法叫做饿汉式,因为不是懒加载的,而是在类加载的过程中就实例化了,而不是在用的时候也即是getInstance的时候才实例化的。有些人就觉得这样一点都不优雅,因为他们觉得对象如果我不使用,你就不应该初始化去占用空间,而是要等到我实际使用的时候才初始化才叫完美。于是有了下面的懒汉式加载写法
第二种写法
public class Singleton02 {
// 1. 构造方法私有化
private Singleton02() {
}
// 2. 持有自身的静态引用,懒加载
private static Singleton02 SINGLETON;
// 3. 提供获取实例的静态方法
// 但是这种是按方式有瑕疵,在并发场景下可能会有问题
// 并发的时候,两个线程同时判断SINGLETON为null,同时最后那个赋值线程赋给的值
// 如何证明只要用多线程初始化实例打印获取到的单例对象的hashcode不一样就行
public static Singleton02 getInstance() {
if (SINGLETON == null) {
SINGLETON = new Singleton02();
}
return SINGLETON;
}
}
可是这种写法其实是错的,因为在并发情况下根本没办法保证返回的单例对象是同一个。于是我们很自然而然的想到加锁的方式