1. 程式人生 > >class檔案結構[2] static final、static、final、普通型別成員變數的賦初值

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

package com.test.a;

publicclass Test {
    
    
privatedouble m1;
    
privatedouble m2=200d;
    
    
privatestatic

double m3;
    
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>將建構函式中的語句和其他型別成員變數的賦值語句合併,這裡設定了m2m5


執行結果如下,可以看到m1m3預設值