在很久很久以前,曾经看过这么一道题目;
package com.tree.thread;
class Singleton
{
private static Singleton singleton = new Singleton();
public static int counter1 ;
public static int counter2 = 0;
Singleton()
{
this.counter1++;
this.counter2++;
}
public static Singleton getInstance()
{
return singleton;
}
}
public class Test
{
public static void main(String[] args)
{
Singleton singleton =Singleton.getInstance();
System.out.println("counter1 = " + singleton.counter1);
System.out.println("counter2 = " + singleton.counter2);
}
}
对该题的输出结果原本的猜测要么是(0,0)要么是(0,1);然后实际操作的结果确是:
可以看出counter1和counter2虽然在直观上感受,初始值都是零,且操作过程完全一样,但却结果迥异。何为?
不妨把做一下处理,使counter俩兄弟的程序流程中的动态变化更具体一点。
package com.tree.thread;
class Singleton
{
private static Singleton singleton = new Singleton();
public static int counter1 ;
public static int counter2 = 0;
Singleton()
{
System.out.println("构造器 开始counter1 = " + singleton.counter1);
System.out.println("构造器开始counter2 = " + singleton.counter2);
this.counter1++;
this.counter2++;
System.out.println("构造器完成counter1 = " + singleton.counter1);
System.out.println("构造器完成counter2 = " + singleton.counter2);
}
public static Singleton getInstance()
{
System.out.println("静态方法中counter1 = " + singleton.counter1);
System.out.println("静态方法中counter2 = " + singleton.counter2);
return singleton;
}
}
public class Test
{
public static void main(String[] args)
{
Singleton singleton =Singleton.getInstance();
System.out.println("counter1 = " + singleton.counter1);
System.out.println("counter2 = " + singleton.counter2);
}
}
所得的结果如下:
由此,也可以知道counter俩兄弟在程序中的实际情况了。
首先,jvm规范规定,只有首次使用时虚拟机才会去加载类。通过main中:
Singleton singleton=Singleton.getInstance();
虚拟机首次调用Singleton类,虚拟机加载相关的类信息,并进行静态域的初始化。
根据规范,引用类型的初始化值为null,基本类型的初始化值是其默认值,且初始化顺序遵循其在程序中出现的顺序。程序开始时的默认值为:
singleton = null;
Counter1 = 0;
Counter2 = 0;
然后,初始化singleton,就要执行new方法,调用构造器,此时静态(类)变量counter1=1;counter2=1。再进行counter1和counter2的初始化,counter1并未显式的赋初始值,故仍为1,而counter2显式的赋初始值,故为0。所以得到上述输出结果。
正是因为初始化的顺序导致了以上的输出结果,当singleton初始化之后,counter1和counter2才随后跟上,导致了counter俩兄弟输出结果的迥异。
如果改变初始化顺序,如:
则结果: