java中final屬型及初始化問題

1.被final修飾而沒有被static修飾的類成員變量只能在三種情況下初始化:
/*
1.創建的時候立即初始化
2.創建之後由構造方法初始化
3.創建之後由代碼塊初始化
*/
 
class Test {
    final int b=2;
    final int c;
    public abc() {
        c=3;
    }
    final int a;
    {
        a = 1;
    }
}
public class TestMode {
    static abc abc=new abc();
    public static void main(String[] args) {
        System.out.println(Test.a);
        System.out.println(Test.b);
        System.out.println(Test.c);
    }
}
2.被final修飾的類屬性會被作爲編譯期常量加入常量池,以後訪問對應類的常量池,不會在常量池中保存一個指向Test類a字段的符號引用,不觸發類的初始化:
class Test {
    final static int a = 1;
    static {
        System.out.println("訪問編譯期常量不觸發類初始化");
    }
}
 
public class TestMode {
 
    public static void main(String[] args) {
        System.out.println(Test.a);
    }
}
查看 TestMode 類的常量池,如下: 

查看 Test 類的常量池如下:

 

我們將代碼修改一下如下:

class Test {
    final static int a = 1;
    static int b = 2;
    static {
        System.out.println("訪問編譯期常量不觸發類初始化");
    }
}
 
public class TestMode {
 
    public static void main(String[] args) {
        System.out.println(Test.a);
        System.out.println(Test.b);
    }
}
查看 Test 類的常量池,如下: 

此時我們發現只出現了1沒有出現2,也就是說非final是不存在常量池中的

 查看 TestMode 類的常量池,如下:

我們發現常量池多了28~33這段內容:
=================================================================================

CONSTANT_Class_info: 類或接口的符號引用,其中的 index 指向字符串字面量的索引。

CONSTANT_Fieldref:字段的符號引用。

CONSTANT_NameAndType_info:字段或方法的部分符號引用。

這樣看來的確是觸發了類的初始化。
注意:33行的 Utf8 I 表示該值的類型:

其中 ACC_STATIC 和 ACC_FINAL 是訪問標誌。

而在 Test 類中,final static int a = 1 會存在 CONSTANT_Integer_info(該表中不存在符號引用,只有具體的值)中

=================================================================================

再看如下:

/*
這段代碼去掉final之後結果完全不一樣
*/
public class TestMode {
 
    public static void main(String[] args) {
        final String a = "1";
        final String b = "2";
        String c = a + b;
        String d = "12";
        System.out.println(c == d);//返回true,證明a,b的確作爲編譯期常量
                                   //加入了常量池,而c也在編譯期確定其值
    }
}
以上即爲 final 所修飾屬性特點。

3.看一個面試題加深理解
class Price {
    static Price P = new Price(2.7);
    static double apple = 20;//加上final後 輸出結果爲17.3
    double Price;
 
    public Price(double orange) {
        Price = apple - orange;
    }
}
 
public class PriceTest {
    public static void main(String[] args) {
        System.out.println(Price.P.Price);//結果爲-2.7
 
    }
 
}
這個程序中,在類加載階段的準備階段p和apple會被編譯器賦予對應類型的默認初值(null和0.0),在隨後的類加載的初始化階段,由於static字段執行順序是由字段在源文件中出現的順序決定的,所以會先執行new Price(2.7),分配對象空間並對其做初始化,在這個時候apple的值還是0.0,所以最終結果爲-2.7。

當使用final時,字面量20會在編譯期加入Price類的常量池中的CONSTANT_Double_info,在遇到final字段時,在編譯時編譯器將會爲該靜態屬性賦予ConstantValue屬性,ConstantValue屬性的作用是通知虛擬機自動爲靜態變量賦值 ,對於類變量,有兩種方式賦值:在類構造器<clinit>方法中或者使用ConstantValue屬性,具有該屬性的靜態字段將會在類加載的準備階段被賦予所指定的的值(這也是final static 字段必須手動賦值的原因)。所以最終結果爲17.3。


————————————————
版權聲明:本文爲CSDN博主「myllxy」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_39327985/article/details/81839685

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章