1. 程式人生 > >[Java] 基本資料型別對常量池的使用

[Java] 基本資料型別對常量池的使用

1 常量池是什麼

.java檔案會編譯為.class檔案,常量池指的是.class檔案中一部分,可以理解為.class檔案的資源倉庫[1]。常量池中主要存放兩類常量:

  • 字面量 (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.