1. 程式人生 > 實用技巧 >Java 語言中涉及到的各種常量池

Java 語言中涉及到的各種常量池

參考文獻:

https://blog.csdn.net/u011635492/article/details/81046174

Class檔案常量池 / 位元組碼常量池


Class 檔案常量池指的是編譯生成的 class 位元組碼檔案,其結構中有一項是常量池(Constant Pool Table),用於存放編譯期生成的各種字面量和符號引用,這部分內容將在類載入後進入方法區的執行時常量池中存放。

這裡的字面量是指字串字面量和宣告為 final 的(基本資料型別)常量值,這些字串字面量除了類中所有雙引號括起來的字串(包括方法體內的),還包括所有用到的類名、方法的名字和這些類與方法的字串描述、欄位(成員變數)的名稱和描述符;宣告為final的常量值指的是成員變數,不包含本地變數,本地變數是屬於方法的。這些都在常量池的 UTF-8 表中(邏輯上的劃分);

符號引用,就是指指向 UTF-8 表中向這些字面量的引用,包括類和介面的全限定名(包括包路徑的完整名)、欄位的名稱和描述符、方法的名稱和描述符。只不過是以一組符號來描述所引用的目標,和記憶體並無關,所以稱為符號引用,直接指向記憶體中某一地址的引用稱為直接引用;

你可以通過 javap -v 對位元組碼進行反編譯,可能會看到如下輸出:

這就是所謂的位元組碼常量池。

執行時常量池


執行時常量池是方法區的一部分,是一塊記憶體區域。Class 檔案常量池將在類載入後進入方法區的執行時常量池中存放。一個類載入到 JVM 中後對應一個執行時常量池,執行時常量池相對於 Class 檔案常量池來說具備動態性,Class 檔案常量只是一個靜態儲存結構,裡面的引用都是符號引用。而執行時常量池可以在執行期間將符號引用解析為直接引用。可以說執行時常量池就是用來索引和查詢欄位和方法名稱和描述符的。給定任意一個方法或欄位的索引,通過這個索引最終可得到該方法或欄位所屬的型別資訊和名稱及描述符資訊,這涉及到方法的呼叫和欄位獲取。

字串池


字串池是全域性的,JVM 中獨此一份,因此也稱為全域性字串常量池。執行時常量池中的字串字面量若是成員的,則在類的載入初始化階段就使用到了字串常量池;若是本地的,則在使用到的時候(執行此程式碼時)才會使用到字串常量池。其實,“使用常量池”對應的位元組碼是一個ldc指令,在給 String 型別的引用賦值的時候會先執行這個指令,看常量池中是否存在這個字串物件的引用,若有就直接返回這個引用,若沒有,就在堆裡建立這個字串物件並在字串常量池中記錄下這個引用(jdk1.7)。String 類的intern()方法還可在執行期間把字串放到字串常量池中。JVM 中除了字串常量池,8種基本資料型別中除了兩種浮點型別剩餘的6種基本資料型別的包裝類,都使用了緩衝池技術,但是 Byte、Short、Integer、Long、Character 這5種整型的包裝類也只是在對應值在 [-128,127] 時才會使用緩衝池,超出此範圍仍然會去建立新的物件。其中:

  • 在 jdk1.6(含)之前也是方法區的一部分,並且其中存放的是字串的例項;
  • 在 jdk1.7(含)之後是在堆記憶體之中,儲存的是字串物件的引用,字串例項是在堆中;
  • jdk1.8 已移除永久代,字串常量池是在本地記憶體當中,儲存的也只是引用。

以上是 intern()方法的文件說明,從中我們可以知道:

返回一個規範化的字串物件代表。

字串池(初始為空)由 String 類私有地維護。

當 intern 方法被呼叫,如果池中已經包含相等的字串物件(依據 equals 方法),那麼該池中字串將被返回。否則,字串物件會被新增到池中,然後返回它的引用。

因此,當且僅當 s.equals(t) 為 true,那麼對於字串 s 和 t,s.intern() == t.intern() 為 true。

返回值:

一個與字串相同內容的字串,但可以保證池中唯一。

請讀者觀看下面的例子體會: