記靜態常量遇到的坑

場景:
public class TppsPath {
    static final String name = "小花";

public class Test {
    public static void main(String args[]){
    System.Out.Println(TppsPath.name);
}
}
某個類引用了該變量值。
由於需求變更,需要更改name = "小明",直接更改了包含此靜態變量的類文件,並編譯成class,把測試環境中相應的class文件替換爲新編譯的文件。(增量部署)。
但是引用的類並不重新編譯,替換。

現象:
測試發現,引用靜態變量的類中,值仍然沒有變過來,還是小花,並不是重新編譯的小明。

解決:

查閱資料發現:
static final常量是在java編譯期就進行賦值的,編譯完成後,Test.class類用反編譯工具打開你會發現是這樣式兒的:
public class Test {
    public static void main(String args[]){
    System.Out.Println("小花");
}
}

所以只替換TppsPath 類的編譯文件的話,並不能改變Test類,因爲他沒有跟着TppsPath 重新編譯,導致數據還是原來的數據。
總結:一旦經static final修飾的常量內容變動的話,需要將所有引用改常量的類同時編譯,並替換。

拓展知識:
static+final
靜態常量,編譯期常量,編譯時就確定值。(Java代碼執行順序,先編譯爲class文件,在用虛擬機加載class文件執行)
放於方法區中的靜態常量池。
在編譯階段存入調用類的常量池中
如果調用此常量的類不是定義常量的類,那麼不會初始化定義常量的類,因爲在編譯階段通過常量傳播優化,已經將常量存到調用類的常量池中了

final常量,類加載時確定或者更靠後。
當用final作用於類的成員變量時,成員變量(注意是類的成員變量,局部變量只需要保證在使用之前被初始化賦值即可)必須在定義時或者構造器中進行初始化賦值
對於一個final變量,如果是基本數據類型的變量,則其數值一旦在初始化之後便不能更改;
如果是引用類型的變量,則在對其初始化之後便不能再讓其指向另一個對象。但是它指向的對象的內容是可變的
原文鏈接:https://blog.csdn.net/u011299745/article/details/52887825

static final常量虛擬機實現:
1.Class 文件的生成階段
Sun javac編譯器對於靜態field字段的初始化賦值策略 
目前的Sun javac編譯器的選擇是: 
1.如果使用final和static同時修飾一個field字段,並且這個字段是基本類型或者String類型的,那麼編譯器在編譯這個字段的時候,會在對應的field_info結構體中增加一個ConstantValue類型的結構體,在別的類進行調用進行賦值的時候使用這個ConstantValue進行賦值; 
2.如果該field字段並沒有被final修飾,或者不是基本類型或者String類型,那麼將在類構造方法cinit>()中賦值。

對於上述的public static final init MAX=100; javac編譯器在編譯此field字段構建field_info結構體時,除了訪問標誌、名稱索引、描述符索引外,會增加一個ConstantValue類型的屬性表。
2.類加載階段(鏈接(準備階段))
如果類字段的字段屬性表中存在ConstantValue屬性,即同時被final和static修飾,那麼在準備階段變量value就會被初始化爲ConstValue屬性所指定的值。 假設上面的類變量value被定義爲: public static final int value = 3; 編譯時Javac將會爲value生成ConstantValue屬性,在準備階段虛擬機就會根據ConstantValue的設置將value賦值爲3。 
順便講一句:static final 與final static 只是使用的習慣問題 ,沒有 差別
原文鏈接:https://blog.csdn.net/qq_35495763/article/details/81071936


在java中用final修飾符修飾的變量表示不可以被二次賦值,且系統不會給其賦默認值。
如果單純只是final變量,可以在定義的時候就賦默認值,也可以在構造方法中賦默認值。
但是如果同時用final static 修飾變量,因爲static變量屬於類而不屬於對象,且在調用構造方法之前static 變量就已經被系統給賦默認值。而相應的final static 變量就只能在定義的時候就初始化,否則既無法在構造方法中初始化,系統又不會賦默認值,相當於這個變量被定義出來是毫無用處的。 因此java中final static變量必須初始化。

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