class檔案結構[2] static final、static、final、普通型別成員變數的賦初值
不考慮作用域修飾符,成員變數的型別有:
static final 型別、僅static型別、僅final型別、普通型別
並且,我們在定義成員變數的時候,還可以通過=賦初值。(其中包含final時強制要求賦初值)
那麼,這些賦值操作都是在哪個階段完成的呢?
按照執行的先後順序
1.對於staticfinal型別成員變數,會在class檔案的Fields中增加ConstantValue屬性,在準備階段,就由虛擬機器設定好常量值
2.對於僅static型別成員變數,會在準備階段,設定預設初始值,在初始化階段,將賦值語句和static{}程式碼塊合併成<clinit>方法,在<clinit>
3.對於僅final型別成員變數、普通成員變數,會將賦值語句和原建構函式合併,在新建構函式中賦值 (雖然僅final型別成員變數也有ConstantValue屬性,但是其作為例項成員變數,只有在堆上分配了物件以後才能賦值,所以只能是到了這裡才賦值。所以此時的ConstantValue屬性其實並沒有什麼用)
僅final型別成員變數,編譯器會要求要麼在定義時賦初值,要麼在建構函式中賦初值。
普通成員變數,如果沒有設定初始值,也會為其設定預設初始值。(應該也是在上述的第3階段中,在堆中分配了物件的記憶體空間以後)
預設初始值的規則如下
型別 |
預設初始值 |
int/byte/short/char/boolean |
0 |
long |
0 |
float |
0 |
double |
0 |
引用 |
null |
以如下程式碼為例(使用double型別驗證,因為int型別會直接將數值生成到位元組碼指令中去)
Java Code
1 |
package com.test.a; privatestaticdouble m4=400d; privatefinaldouble m5=500d; publicstaticfinaldouble m6=600d; static{ m4=700d; } public Test(){ m2=800d; } publicvoid printUnsignedMember(){ System.out.println("m1="+m1); System.out.println("m3="+m3); } } |
常量池中的內容為
m6具有ConstantValue屬性,指向常量池中的600d,會在準備階段就設定初始值。
<clinit>將僅static型別成員變數的賦值語句和static{}靜態程式碼塊合併,這裡設定了m4
<init>將建構函式中的語句和其他型別成員變數的賦值語句合併,這裡設定了m2、m5
執行結果如下,可以看到m1、m3預設值