单例模式有多种多样的实现方式,根据剑指offer中的观点,有三种实现方式比较推荐,第一种是双重检验锁的方式,第二种是使用静态代码块,让类在初始化的时候就完成单例变量的赋值,第三种是最推荐的,使用静态内部类。
这里只分析静态内部类实现方式中的类初始化过程,以便更好地理解单例模式,静态内部类的基本概念请自行谷歌。
普通静态内部类的初始化顺序
外部类和内部类定义:
public class OutClass {
static{
System.out.println("outclass invoke");
}
private OutClass(){
System.out.println("built outclass");
}
public static class InnerClass{
static{
System.out.println("innerclass invoke");
}
InnerClass(){
System.out.println("built innerclass");
}
}
}
测试代码:
/**
* 输出结果:
* innerclass invoke
* built innerclass
* OutClass$InnerClass@15db9742
* @author hzf
*
*/
public class Test {
public static void main(String[] args) {
OutClass.InnerClass innerClass = new OutClass.InnerClass();
System.out.println(innerClass);
}
}
过程分析:
结果显示是只初始化了静态内部类,所以调用静态内部类的语句看起来跟外部类有关系,实际在类初始化的时候,跟外部类一点关系都没有,另外,如果把内部类的权限修饰符从public改为private,静态内部类的实例连创建都没法创建,所以笔者对静态内部类的理解是静态内部类和其外部类其实是同等级的,静态内部类只是藏在外部类里面,被外部类保护起来,用户没办法自由地使用它(通过权限修饰符来控制静态内部类的访问权限)。
利用静态内部类实现单例模式
在上面实现单例模式的三种推荐方法中,最简单的方式是使用静态代码块,在类初始化的时候就给单例变量赋值,但是这个方法有一个缺陷,静态代码块在类初始化就会执行,所以单例变量不是在第一次使用它的时候创建,而是在第一次用到单例变量所在类的时候就被创建,这样就有可能过早地创建单例变量,从而降低内存的使用效率。这时利用静态内部类就是好的选择,将单例变量藏在静态内部类中,可以保证当外部类(单例变量所在类)被初始化的时候,跟静态内部类没有任何关系,只有使用静态内部类的时候才会初始化单例变量,也能通过权限修饰符控制静态内部类的访问权限。
代码实现:
public class OutClass {
static{
System.out.println("outclass invoke");
}
private OutClass(){
System.out.println("built outclass");
}
public static OutClass getInstance(){
return InnerClass.outClass;
}
private static class InnerClass{
static{
System.out.println("innerclass invoke");
}
InnerClass(){
System.out.println("built innerclass");
}
private static OutClass outClass = new OutClass();
}
}
/**
* 输出结果:
* outclass invoke
* innerclass invoke
* built outclass
* OutClass@15db9742
* @author hzf
*
*/
public class Test {
public static void main(String[] args) {
System.out.println(OutClass.getInstance());
}
}
结果分析:
主函数调用外部类的静态函数,所以先初始化外部类,之后外部类调用内部类的静态变量,初始化内部类,最后调用外部类的构造方法。