1. 程式人生 > >靜態變數初始化:靜態變數和靜態程式碼塊初始順序

靜態變數初始化:靜態變數和靜態程式碼塊初始順序

在工具類中,通常會初始化一些單例變數,這些變數由於只會初始一次,並不適合放在建構函式中,因此通常選擇在靜態程式碼塊中執行,那麼在一個類中,就會涉及到靜態變數和靜態程式碼塊的初始化執行順序問題。

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。