示例一
代码清单
如下所示代码和对应的输出结果
public class MyTest6 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
System.out.println("counter1: " + Singleton.counter1);
System.out.println("counter2:" + Singleton.counter2);
}
}
class Singleton{
public static int counter1;
public static int counter2 = 1;
private static Singleton singleton = new Singleton();
public Singleton() {
counter1++;
counter2++;
}
public static Singleton getInstance(){
return singleton;
}
}
输出结果:
counter1: 1
counter2:2
结果分析
我们结合类的加载、链接、初始化和使用四个阶段对这段程序进行分析。
- 首先,
MyTest6
这个类是启动类,JVM会调用该类的main方法,即会主动使用该类,MyTest6
经历加载、链接和初始化。 - 执行
main
方法过程中发现需要调用类Singleton
的静态方法getInstance
,是对Singleton
类的主动使用,因此JVM首先会将Singleton
对应的class文件加载到内存中来。 - 接着执行链接过程,在链接过程中先根据JVM规范对
Singleton
的class文件进行校验。然后按照定义的顺序为Singleton
类中的静态变量赋予默认的值,即为Singleton.counter1
赋默认值0,为Singleton.counter2
赋默认值0,为Singleton.singleton
赋默认值null。链接的最后一步将Singleton
类中的符号引用转换为直接引用。 - 紧接着按照
Singleton
中静态变量和静态代码块的定义顺序执行初始化过程,在这里Singleton.counter1
还是默认值0,Singleton.counter2
被初始化为1,然后执行Singleton
的构造方法构造Singleton
对象,在构造方法中对counter1
和counter2
执行++操作,然后用生成的Singleton
对象来初始化singleton
静态变量。 - 接下来就可以正常使用
Singleton
类了。
示例二
代码清单
如下所示的代码和输出结果,代码只是移动了示例一种一行代码的位置,最后输出的结果就不一样了:
public class MyTest6 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
System.out.println("counter1: " + Singleton.counter1);
System.out.println("counter2:" + Singleton.counter2);
}
}
class Singleton{
public static int counter1;
private static Singleton singleton = new Singleton();
public Singleton() {
counter1++;
counter2++;
}
public static int counter2 = 0;
public static Singleton getInstance(){
return singleton;
}
}
输出结果:
counter1: 1
counter2:0
结果分析
MyTest6
的加载、链接和初始化过程同示例一。Singleton
类的加载过程同示例一。Singleton
的链接过程同示例一。只不过static
成员的赋默认值的顺序稍有不同,示例一中先给counter2
赋默认值0,再给singleton
赋默认值null
,示例二中先给singleton
赋默认值null
,再给counter2
赋默认值0,结果都一样,只不过按照static
成员的定义顺序赋默认值。Singleton
的初始化过程同样按照static
成员的定义顺序进行初始化。counter1
保持默认值0,接着执行Singleton
的构造方法构造Singleton
对象,构造方法执行之前counter1
和counter2
的值都为默认值0,执行完构造方法后counter1
和couner2
的值都变为1,对象构造完后将singleton
初始化为构造好的Singleton
对象,最后执行counter2
的初始化过程,将counter2
的值重新赋值为0。