[Java] 基本資料型別對常量池的使用
1 常量池是什麼
.java
檔案會編譯為.class
檔案,常量池指的是.class
檔案中一部分,可以理解為.class
檔案的資源倉庫
- 字面量 (Literal)
- 文字字串、宣告為final的常量值等
- 符號引用 (Symbolic Reference)
- 類和介面的全限定名 (Fully Qualified Name)
- 欄位(field)的名稱和描述符(Descriptor)
- 方法(method)的名稱和描述符
在常量池中,所有型別的常量均存放在表結構中,但是不同的常量型別對應的表結構也不相同, 具體結構需要參考Java Language Specification。
在Java程式執行的過程中,上述常量池會載入到虛擬幾種,成為執行時常量池(Runtime Constant Pool)。
1.1 基本資料型別的包裝類對常量池的使用
Java程式中基本型別的變數可以直接在常量池中讀取字面量。基本型別的包裝類的大部分都實現了常量池技術,這些類是Byte,Short,Integer,Long,Character,Boolean
。另外Byte,Short,Integer,Long,Character
這5種整型的包裝類只是在對應值小於等於127時才可使用常量池,見示例程式碼內的註釋:
public class Test{
Integer i1=new Integer(1);
Integer i2=new Integer(1);
//i1,i2物件具有不同的記憶體地址
System.out.println(i1==i2);//輸出false
Integer i3=1;
Integer i4=1;
//i3,i4指向常量池中同一個字面值,即同一個記憶體位置,因此下面語句返回true
System.out.println(i3==i4);//輸出true
//而i1,i3位於不同的記憶體位置,會返回false
System.out.println(i1==i3);//輸出false
}
但是如果上述程式碼中,int的值大於127或者小於-128,那麼變數不會引用常量池中的字面量,而是建立新的變數。
Integer i1 = 1111 ;
Integer i2 = 1111;
//由於常量值對於127,不會使用常量池技術,因此輸出false
System.out.println(i1==i2);
//但是兩個變數的內容是相等的,因此輸出true
System.out.println(i1.equals(i2));
2 模擬基本資料型別對常量池的使用
在編譯過程中,編譯器將Integer i = 5
之類的語句編譯為Integer i = Integer.valueOf(5)
。實際上,Java正是通過Iteger.valueOf(int i)
方法實現了基本資料型別的自動裝箱。同時,在valueOf()
方法中使用常量池技術。程式碼模擬如下:
public static Integer valueOf(int i) {
final int offset = 128;
if (i >= -128 && i <= 127) { // must cache
return IntegerCache.cache[i + offset];
}
return new Integer(i);
}
private static class IntegerCache {
private IntegerCache(){};
static final Integer cache[] = new Integer[-(-128) + 127 + 1];
static {
for(int i = 0; i < cache.length; i++){
cache[i] = new Integer(i - 128);
}
}
}
上述程式碼中valueOf()
方法首先判斷資料是否小於127且大於-128,如果符合條件,那麼直接返回已經快取好的物件,這些物件在類載入時候由static
語句塊(一般用於初始化靜態變數)初始化。可以看出,相同字面值的物件具有相同的記憶體地址。
參考文獻
[1] 周志明. 深入理解Java虛擬機器[M]. 機械工業出版社, 2011.