介绍之前先简单的说一下内部类:
无论是静态内部类还是非静态内部类,在类初始化的时候都不会被加载
package duoxiancheng.single.neibulei;
public class ClassMonitor {
/*
//1. 如果在懒汉单例模式里面添加一个它的全局属性
//2. 如果想获取它的yourClass 可以调用直接调用yourClass
//3. 但是这样对象会初始化,构造方法初始化,我不想让类初始化怎么办
public static String yourClass = "通信工程1班";
private static ClassMonitor classMonitor = new ClassMonitor();
private ClassMonitor() {
System.out.println("构造方法初始化");
}
public static ClassMonitor getClassMonitor() {
System.out.println("获取对象");
return classMonitor;
}
public static void main(String[] args) {
System.out.println(yourClass);
}
*/
/*
1. 当执行getInstance()的时候才会调用内部类里面的ClassMonitor实例
2. 此时,内部类会被加载到内存中,在类加载的时候对monitor进行初始化
3. 而且直接调用yourClass的时候不会对类初始化
4. 这是懒汉模式和恶汉模式的结合体,而且线程安全
*/
public static String yourClass = "通信工程";
//静态内部类,用来创建班长对象
private static class MonitorCreator {
private static ClassMonitor classMonitor = new ClassMonitor();
}
private ClassMonitor() {
System.out.println("构造方法初始化");
}
public static ClassMonitor getInstance() {
System.out.println("静态内部类获取");
return MonitorCreator.classMonitor;
}
public static void main(String[] args) {
System.out.println(yourClass);
}
}
为什么静态内部类能保证单例?
我们再回头看下getInstance()方法,调用的是MonitorCreator.classMonitor,
取的是SingleTonHoler里的INSTANCE对象,
跟上面那个DCL方法不同的是,getInstance()方法并没有多次去new对象,
故不管多少个线程去调用getInstance()方法,取的都是同一个INSTANCE对象,而不用去重新创建。
当getInstance()方法被调用时,SingleTonHoler才在SingleTon的运行时常量池里,把符号引用替换为直接引用,
这时静态对象INSTANCE也真正被创建,然后再被getInstance()方法返回出去,这点同饿汉模式。
那么,是不是可以说静态内部类单例就是最完美的单例模式了呢?
其实不然,静态内部类也有着一个致命的缺点,就是传参的问题,由于是静态内部类的形式去创建单例的,
故外部无法传递参数进去,例如Context这种参数,
所以,我们创建单例时,可以在静态内部类与DCL(双重双重锁懒汉模式(Double Check Lock))模式里自己斟酌。
静态内部类实现的单例模式为什么是线程安全的
虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。如果在一个类的<clinit>()方法中有耗时很长的操作,就可能造成多个进程阻塞(需要注意的是,其他线程虽然会被阻塞,但如果执行<clinit>()方法后,其他线程唤醒之后不会再次进入<clinit>()方法。同一个加载器下,一个类型只会初始化一次。),在实际应用中,这种阻塞往往是很隐蔽的。