標籤:
原始碼變成可運行程式,需要經過編譯——>載入——>運行幾個階段。
final修飾的變數必須顯性進行初始化。初始化有三種方式:
(1)直接初始化
(2)動態代碼塊
(3)建構函式
如果一個變數既被final修飾又被static修飾,那麼這個變數一定要被初始化(滿足final特性),另外要麼直接初始化要麼在靜態
代碼塊中進行初始化(滿足static特性)。
final修飾的變數的初始化先於static修飾的變數,static修飾的變數先於普通變數的初始化。
如果是final層級的,那麼雖然也是根據代碼次序的前後順序進行初始化,但是它是在編譯階段進行宏替換,即在編譯階段就知道了值,
因此在載入運行階段對這一先後順序完全感知不到。
但是如果是static層級的,代碼的先後順序會造成程式運行結果的不同。因為它們都是在載入進行的時候進行初始化的。
歸結如下:
(1)final修飾的變數在編譯階段完成
(2)static修飾的變數在一個靜態代碼塊中進行初始化
(3)普通變數在建構函式中進行初始化
1 class Price { 2 final static Price INSTANCE=new Price(2.8); 3 final static double initPrice=20; 4 double currentPrice; 5 public Price(double discount) { 6 currentPrice=initPrice-discount; 7 } 8 } 9 10 public class Test {11 public static void main(String[]args) {12 System.out.println(Price.INSTANCE.currentPrice);13 }14 }
運行結果:17.2
1 class Price { 2 static Price INSTANCE=new Price(2.8); 3 static double initPrice=20; 4 double currentPrice; 5 public Price(double discount) { 6 currentPrice=initPrice-discount; 7 } 8 } 9 10 public class Test {11 public static void main(String[]args) {12 System.out.println(Price.INSTANCE.currentPrice);13 }14 }
運行結果:-2.8
1 class Price { 2 static double initPrice=20; 3 static Price INSTANCE=new Price(2.8); 4 double currentPrice; 5 public Price(double discount) { 6 currentPrice=initPrice-discount; 7 } 8 } 9 10 public class Test {11 public static void main(String[]args) {12 System.out.println(Price.INSTANCE.currentPrice);13 }14 }
運行結果:17.2
1 class Price { 2 Price INSTANCE=new Price(2.8); 3 public Price(double discount) { 4 5 } 6 } 7 8 public class Test { 9 public static void main(String[]args) {10 new Price(29);11 }12 }
運行結果:Exception in thread "main" java.lang.StackOverflowError
程式分析對比:
這幾個程式,其實就是要得出currentPrice變數的結果,它是一個普通變數,因此它是在建構函式當中進行初始化的。要得出currentPrice得先有INSTANCE,
INSTANCE就是一個Price的執行個體,要有INSTANCE就得先調用建構函式。建構函式就會去看此時的initPrice和discount變數的初始化值。
對於程式(1)因為變數都是final修飾的,因此代碼的前後順序對於載入運行時的代碼來說初始化順序都是一致的。
對於程式(2)因為變數是由static修飾,又因為變數INSTANCE的代碼順序先於initPrice,因此INSTANCE的初始先於initPrice的初始化,這是運行時代碼可見的。
於是當程式先初始化INSTANCE,INSTANCE的初始化需要initPrice和discount兩個變數,而此時initPirce變數還沒有顯性的被代碼初始化,而只是系統初始化,因此
此時initPrice此時的值為0。所以結果為-2.8。
對於程式(3)變數同樣是由static修飾的,變數initPrice的代碼順序先於變數INSTANCE,因此initPrice的初始化先於INSTANCE的初始化。於是當程式需要得到currentPrice的值時,
需要調用INSTANCE變數,而INSTANCE的變數這時會去查看initPrice的值,此時它已經初始化,因此結果為17.2。
對於程式(4)當我們new執行個體時,就是調用建構函式給執行個體變數進行初始化。而普通變數的初始化時將其拿到建構函式當中來實現的。相當於上面的代碼變成了:
1 class Price {2 public Price(double discount) {3 Price INSTANCE=new Price(2.8);4 }5 }
外面調用建構函式,而建構函式又調用自身。因此一直在調用建構函式,一直入棧。造成棧溢出。
上面的程式產生的結果發生在代碼載入階段,因此才會出現static代碼先後順序的問題;如果代碼是由執行個體調用的那麼,因代碼順序不同而導致的初始化順序不一致問題會不可見。即對於
執行個體調用階段的代碼初始化是一致的。正如,final對於載入階段是一致的一樣。
java變數初始化