版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Insert_day/article/details/70145374
静态内部类实现是我个人比较推荐的,其实现如下:
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton() {}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
上面实现中,通过声明一个静态内部类
SingletonHolder
来持有单例
INSTANCE
,在
getInstance()
方法中返回该实例。
我们知道,静态内部类与非静态内部类之间有很大的区别,非静态内部类在编译完成之后会隐含地保存着一个
指向创建它的外围类的
引用,但是静态内部类却没有。这意味着
静态内部类
的创建是不需要依赖于外围类,
它不能使用任何外围类的非static成员变量和方法,某种程度上来说,静态内部类可以当成是一个独立的类。它的初始化和外部类初始化无关。
下面详细介绍一下它是如何解决单例中的各种问题的。
首先,它是如何实现延迟加载的?
由于
INSTANCE
在静态内部类中声明并实例化,在
【单例深思】饿汉式与类加载 中我们知道有且只有5种情况
会引发初始化操作,
初始化阶段是执行类构造器<clinit>()
方法的过程。
<clinit>()
方法是由编译器自动收集类中的所有类变量(static)的赋值动作和静态语句块(static{})块中的语句合并产生的。因为外部类初始化
不会引发静态内部类
SingletonHolder
的初始化,所以只有真正调用
getInstance()
方法,执行
SingletonHolder.
INSTANCE
时,才会引发静态内部类初始化,从而完成实例化。所以,静态内部类实现是延迟加载的。
它如何保证实例化时线程安全?
既然静态内部类初始化可以当成为一个普通类的初始化,根据
在
【单例深思】饿汉式与类加载 中所描述的类加载过程,可以知道
虚拟机会保证一个类的
<clinit>()
方法在多线程环境中被正确的加锁、同步。所以,
静态内部类实现是在
实例化时是线程安全
。
在其他方面,由于在读取实例的时候不会进行同步,所以没有性能缺陷;没有使用
volatile ,也不依赖 JDK 版本。
总之,静态内部类实现单例由于编写简单,满足线程安全和延迟加载,无其他明显缺点,是一种在工作中用的比较多的方式。