編譯時常量和運行時常量分析


今天偶然遇到一個編譯時常量(Compile-time Constant)和運行時常量(Run-time Constant)的題目,所以希望通過寫一篇博文, 來仔細記錄下所有的區別和優劣.

因水平有限, 大部分內容都是查閱資料和其他人的博客來的. 

問題起源

首先, 源於在博客https://blog.csdn.net/hzw19920329/article/details/51055736 看到了這個代碼

public class GetClass {  
    public static void main(String[] args) {  
        System.out.println(Test.name);//①  
        System.out.println(Test.score);//②  
        System.out.println(Test.age);//③  
    }  
}  
class Test  
{  
    public static int age = 23;  
    public static final String name = "shanxi";  
    public static final Integer score = 85;  
    static   
    {  
        System.out.println("Test static block");  
    }  
}

  

單獨運行①輸出的結果是: shanxi

單獨運行②輸出的結果是: 

Test static block
85

單獨運行③輸出的結果是:

Test static block
23

 

於是開始思考,為什么會這樣,原博主也解釋了說是"可以看出雖然兩者都是static final 類型的,但是name作為"編譯常量",他是不需要對類Test進行初始化就可以讀取,因而不會執行Test中的靜態代碼塊;但是score雖然作為static final變量,但是他並不是"編譯常量",需要初始化Test類之后才可以,因而會首先執行static代碼塊,隨后輸出score的值;對於非final的static域,那么對他訪問之前要先進行鏈接(為這個域初始化空間)和初始化(初始化該存儲空間),也就明白了單獨運行③會首先輸出static塊的值原因;"

開始解決

第一步, 什么是編譯常量

Oracle的官方文檔 15.28 Constant Expressions 詳細的解釋了,什么是 Compile-time Constant

1. 原始類型字面量,或者String字面量
2. 能轉型為原始類型字面量,或String字面量的常量
3. 一元運算符(+,-,~,!,但不包含++, --) 和1,2組成的表達式 
4. 多元運算符(*,/和%)和1,2組成的表達式
5. 附加運算符( additive operators) (+ 或 -)與之前幾條組成的表達式
6. 位移運算符(<<,>>, >>>)和之前幾條組成的表達式
7. 關系運算符(<,<=,>,>= ,不包括 instanceof)與之前幾條組成的表達式
8. 關系運算符(==,!=)與之前幾條組成的表達式
9. 位運算符(&, ^, |)與之前幾條組成的表達式
10. 條件與和條件或運算符(&&, ||) 與之前幾條組成的表達式
11. 三元運算符 (?:)和之前幾條組成的表達式
12. 帶括號的表達式,括號內也是常量表達式
13. 引用常量變量的簡單變量 
14. 類中的常量變量引用,使用類的全限定名或類名進行引用(String.class)

針對其簡單解釋其中幾個

1, 編譯時常量必須定義為基本類型或者String,

    基本類型指的是int, long, short, boolean, char, float, double

    所以, 如果Test類中如果再定義一個public static final int b=2; 那么Test.b輸出時,一樣不會加載Test類, 而是直接輸出2.不會輸出靜態代碼塊.

2, 編譯時常量也可以是基本類型加上部分運算符,   上面的規則我基本都試過了, 都是對的. 其實只要記住, 運算符只要不是instanceof和自加, 自減運算符就基本都可以. 這樣在筆試的時候, 可以記得更清楚. 

3.其實上面介紹中少了最重要的一項, 變量必須是用final修飾的, 而且變量必須在聲明的同時進行賦值(其實這是final修飾詞的要求)


編譯時常量是否一定需要static修飾?

這個問題值得深究, 分兩方面來解釋這個問題

  1. 如果是要外部調用, 那沒有static是不行的, 這個很好解釋, 如果沒有static, 那么想要得到這個常量,必須得new出來對象才行, 所以外部調用時, 必須要用static修飾
  2. 內部使用, 內部使用時, static為非必須存在的. 

可能的面試題: 編譯時常量存在什么樣的風險?

(CSDN中別人這么說的)

編譯時常量在編譯的時候會被直接寫成對應的值, 而不會再從原來的類中讀取, 這樣就會導致問題的產生:

如果A類定義了常量, B類使用了常量, 並且都進行了編譯, 當A類的源碼被改動了, 常量的值發生了變化, 我們對A類進行重新編譯, 但是沒有對B進行重新編譯, 那么B類中用到的是原來A類中的常量值, 即舊值, 這樣就導致了風險的產生.  

但是理論上是這樣的, 實際上操作中IDE會自動幫我們重新編譯B類, 所以我試着操作了幾次, 並沒有發生這樣的風險, 

但是這個風險的確是存在的.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM