在工具类中,通常会初始化一些单例变量,这些变量由于只会初始一次,并不适合放在构造函数中,因此通常选择在静态代码块中执行,那么在一个类中,就会涉及到静态变量和静态代码块的初始化执行顺序问题。
public class Test
{
private static Test t = new Test();
private static String a = "1";
static
{
System.out.println(Test.a);
Test.a = "2";
System.out.println(Test.a);
System.out.println(Test.b);
Test.b = "4";
System.out.println(Test.b);
}
private static String b = "3";
private Test()
{
System.out.println("Constructor");
System.out.println(Test.a);
System.out.println(Test.b);
}
public static void start()
{
}
public static void main(String[] args)
{
Test.start();
}
}
上面的代码示例中,定义了3 个静态变量和一个静态代码块。执行输出如下:
根据输出显示:
首先调用构造函数Test() 初始化t,此时打印a,b的值为null,说明此时,a,b还未被初始化赋值。
打印结果1说明在执行完构造函数之后,a被初始化为1。改变a值为2,当打印a时,输出2.
接着打印b,却发现输出为null,说明此时b还未被初始化。
给b赋值为4,打印结果输出4.
那么b 在什么时候初始化呢,其实是在执行完第二个system.out.println(b)时,就开始初始化b为3.
可以增加第二块静态代码块进行验证:
public class Test
{
private static Test t = new Test();
private static String a = "1";
static
{
System.out.println(Test.a);
Test.a = "2";
System.out.println(Test.a);
System.out.println(Test.b);
Test.b = "4";
System.out.println(Test.b);
}
private static String b = "3";
static
{
System.out.println(Test.b);
}
private Test()
{
System.out.println("Constructor");
System.out.println(Test.a);
System.out.println(Test.b);
}
public static void start()
{
}
public static void main(String[] args)
{
Test.start();
}
}
输出结果为
在private static String b = "3" 之后增加第二块代码块之后,打印b值为3,说明b在执行完第二个system.out.println(b)时,被赋值b为3.
由此可见,静态代码块会根据静态变量的声明顺序及静态代码块中自身的代码顺序初始化静态变量。
上述代码等价于下面代码:
public class Test
{
private static Test t ;
private static String a;
static
{
Test.t = new Test();
Test.a = "1";
System.out.println(Test.a);
Test.a = "2";
System.out.println(Test.a);
System.out.println(Test.b);
Test.b = "4";
System.out.println(Test.b);
Test.b = "3";
}
private static String b;
static
{
System.out.println(Test.b);
}
private Test()
{
System.out.println("Constructor");
System.out.println(Test.a);
System.out.println(Test.b);
}
public static void start()
{
}
public static void main(String[] args)
{
Test.start();
}
}
实际上,jvm就是按照上述静态代码块中的顺序初始化静态变量的。
可以执行 javap -c Test查看Test的字节码:
为了简化不必要的指令,删除了所有的打印语句及第二次代码中的追加的静态代码块。从字节码中我们看到,在静态代码块中,首先执行初始化方法,并初始化t。然后依次从常量池中读取字符串常量初始化a,b。