1. 程式人生 > 程式設計 >詳解JAVA 常量池

詳解JAVA 常量池

前言

對常量池的理解之前,需要熟悉的是一些術語:

字面量

在電腦科學中,字面量(literal)是用於表達原始碼中一個固定值的表示法(notation)。
幾乎所有計算機程式語言都具有對基本值的字面量表示,諸如:整數、浮點數以及字串;而有很多也對布林型別和字元型別的值也支援字面量表示;
還有一些甚至對列舉型別的元素以及像陣列、記錄和物件等複合型別的值也支援字面量表示法。C語言關於複合字面量的介紹可參考: [1] 。

百度也給了一個例子:

這個object-c 的例子,容易理解。

#include <stdio.h>
int main(void)
{ 
 int a = 10; // 10為int型別字面量
 char a[] = {"Hello world!"} // Hello world 為字串形式字面量
  .............
 // 以此類推,不再贅述
 return 0;
}

正文

JVM常量池主要分為Class檔案常量池、執行時常量池,全域性字串常量池,以及基本型別包裝類物件常量池。

我在網上找了一個例子:

private int value = 1;
public String s = "abc";
public final static int f = 0x101;
public static void main(String[] args)
{
}
public void setValue(int v){
	final int temp = 3;
	this.value = temp + v;
}
public int getValue(){
	return value;
}

編譯後:

下面只截取了一部分,常量池:

public class test.program
 minor version: 0
 major version: 57
 flags: (0x0021) ACC_PUBLIC,ACC_SUPER
 this_class: #1       // test/program
 super_class: #3       // java/lang/Object
 interfaces: 0,fields: 3,methods: 4,attributes: 1
Constant pool:
 #1 = Class    #2    // test/program
 #2 = Utf8    test/program
 #3 = Class    #4    // java/lang/Object
 #4 = Utf8    java/lang/Object
 #5 = Utf8    value
 #6 = Utf8    I
 #7 = Utf8    s
 #8 = Utf8    Ljava/lang/String;
 #9 = Utf8    f
 #10 = Utf8    ConstantValue
 #11 = Integer   257
 #12 = Utf8    <init>
 #13 = Utf8    ()V
 #14 = Utf8    Code
 #15 = Methodref   #3.#16   // java/lang/Object."<init>":()V
 #16 = NameAndType  #12:#13  // "<init>":()V
 #17 = Fieldref   #1.#18   // test/program.value:I
 #18 = NameAndType  #5:#6   // value:I
 #19 = String    #20   // abc
 #20 = Utf8    abc
 #21 = Fieldref   #1.#22   // test/program.s:Ljava/lang/String;
 #22 = NameAndType  #7:#8   // s:Ljava/lang/String;
 #23 = Utf8    LineNumberTable
 #24 = Utf8    LocalVariableTable
 #25 = Utf8    this
 #26 = Utf8    Ltest/program;
 #27 = Utf8    main
 #28 = Utf8    ([Ljava/lang/String;)V
 #29 = Utf8    args
 #30 = Utf8    [Ljava/lang/String;
 #31 = Utf8    setValue
 #32 = Utf8    (I)V
 #33 = Utf8    v
 #34 = Utf8    temp
 #35 = Utf8    getValue
 #36 = Utf8    ()I
 #37 = Utf8    SourceFile
 #38 = Utf8    program.java

好的下面介紹class 常量池;

class 常量池

主要包括:字面量和符號引用

首先字面量不是全部的字面量,如果不明白什麼是字面值請看上面;

字元字面值:

#7 = Utf8 s
#20 = Utf8 abc

用final修飾的成員變數

#9 = Utf8 f
#11 = Integer 257

大概包含的就是這兩種。

符號引用

符號引用主要設涉及編譯原理方面的概念,包括下面三類常量:

類和介面的全限定名,也就是java/lang/String;這樣,將類名中原來的"."替換為"/"得到的,主要用於在執行時解析得到類的直接引用,像上面

#5 = Class #33 // JavaBasicKnowledge/JavaBean
#33 = Utf8 JavaBasicKnowledge/JavaBean

欄位的名稱和描述符,欄位也就是類或者介面中宣告的變數,包括類級別變數和例項級的變數

#4 = Fieldref #5.#32 // JavaBasicKnowledge/JavaBean.value:I
#5 = Class #33 // JavaBasicKnowledge/JavaBean
#32 = NameAndType #7:#8 // value:I

#7 = Utf8 value
#8 = Utf8 I

//這兩個是區域性變數,值保留欄位名稱
#23 = Utf8 v
#24 = Utf8 temp

可以看到,對於方法中的區域性變數名,class檔案的常量池僅僅儲存欄位名。

方法中的名稱和描述符,也即引數型別+返回值

#21 = Utf8 setValue
#22 = Utf8 (I)V

#25 = Utf8 getValue
#26 = Utf8 ()I

其實並不需要怎麼關注符號引用。

那麼這些class 常量池有什麼好處呢?

執行時常量池是方法區的一部分,所以也是全域性貢獻的,我們知道,jvm在執行某個類的時候,必須經過載入、連結(驗證、準備、解析)、初始化,在第一步載入的時候需要完成:

通過一個類的全限定名來獲取此類的二進位制位元組流

將這個位元組流所代表的靜態儲存結構轉化為方法區的執行時資料結構

在記憶體中生成一個類物件,代表載入的這個類,這個物件是java.lang.Class,它作為方法區這個類的各種資料訪問的入口。

類物件和普通物件是不同的,類物件是在類載入的時候完成的,是jvm建立的並且是單例的,作為這個類和外界互動的入口, 而普通的物件一般是在呼叫new之後建立。

上面的第二條,將class位元組流代表的靜態儲存結構轉化為方法區的執行時資料結構,其中就包含了class檔案常量池進入執行時常量池的過程,這裡需要強調一下不同的類共用一個執行時常量池,同時在進入執行時常量池的過程中,多個class檔案中常量池相同的字串,多個class檔案中常量池中相同的字串只會存在一份在執行時常量池,這也是一種優化。

執行時常量池的作用是儲存java class檔案常量池中的符號資訊,執行時常量池中儲存著一些class檔案中描述的符號引用,同時在類的解析階段還會將這些符號引用翻譯出直接引用(直接指向例項物件的指標,記憶體地址),翻譯出來的直接引用也是儲存在執行時常量池中。

執行時常量池相對於class常量池一大特徵就是具有動態性,java規範並不要求常量只能在執行時才產生,也就是說執行時常量池的內容並不全部來自class常量池,在執行時可以通過程式碼生成常量並將其放入執行時常量池中,這種特性被用的最多的就是String.intern()。

那麼就看下String.intern() 來理解:執行時常量池。

首先看下:

string x="x" 和 String x=new String("x");

有什麼區別?

可以肯定的是他們的值是一樣的。

但是他們執行差別很大。string x="x" 會查詢常量池,如果沒有x的話,那麼會存入常量池,如果有的話,那麼會存在於常量池並進行引用。

而 String x=new String("x") 則只會生成在堆中,而不會和常量池產生聯絡。

注:

常量字串和變數拼接時(如:String str3=baseStr + “01”;)會呼叫stringBuilder.append()在堆上建立新的物件。

那麼String.intern() 是什麼意思呢?這個是會去查詢變數詞中有沒有,如果有的話那麼會返回引用,如果沒有的話,這個和版本有關。

詳解JAVA 常量池

題目

public static void main(String[] args) {
// write your code here
	Integer i01=59;
	int i02=59;
	Integer i03=Integer.valueOf(59);
	Integer i04= new Integer(59);
}

public static Integer valueOf(int i) {
	if (i >= IntegerCache.low && i <= IntegerCache.high)
		return IntegerCache.cache[i + (-IntegerCache.low)];
	return new Integer(i);
}

問題如下:

i01 是否和 i02 相等?

i03 是否和 i01 相等。

i04 是否和 i01相等。

總結

寫的比較倉促,後續會完善好。

以上就是詳解JAVA 常量池的詳細內容,更多關於java 常量池的資料請關注我們其它相關文章!