在工具類中,通常會初始化一些單例變量,這些變量由于隻會初始一次,并不适合放在構造函數中,是以通常選擇在靜态代碼塊中執行,那麼在一個類中,就會涉及到靜态變量和靜态代碼塊的初始化執行順序問題。
-
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。