java常量池技術

水貨大咖發表於2016-05-20

簡介

    常量池在java用於儲存在編譯期已確定的,已編譯的class檔案中的一份資料。它包括了關於類,方法,介面等中的常量,也包括字串常量,如String s = “java”這種申明方式;當然也可擴充,執行器產生的常量也會放入常量池,故認為常量池是JVM的一塊特殊的記憶體空間。

Java是一種動態連結的語言,常量池的作用非常重要,常量池中除了包含程式碼中所定義的各種基本型別(如int、long等等)和物件型(如String及陣列)的常量值外,還包含一些以文字形式出現的符號引用,比如:
類和介面的全限定名;
欄位的名稱和描述符;
方法的名稱和描述符。
在C語言中,如果一個程式要呼叫其它庫中的函式,在連結時,該函式在庫中的位置(即相對於庫檔案開頭的偏移量)會被寫在程式中,在執行時,直接去這個地址呼叫函式;
而在Java語言中不是這樣,一切都是動態的。編譯時,如果發現對其它類方法的呼叫或者對其它類欄位的引用的語句,記錄進class檔案中的只能是一個文字形式的符號引用,在連線過程中,虛擬機器根據這個文字資訊去查詢對應的方法或欄位。
所以,與Java語言中的所謂“常量”不同,class檔案中的“常量”內容很豐富,這些常量集中在class中的一個區域存放,一個緊接著一個,這裡就稱為“常量池”。
java中基本型別的包裝類的大部分都實現了常量池技術,這些類是Byte,Short,Integer,Long,Character,Boolean,另外兩種浮點數型別的包裝類則沒有實現。另外Byte,Short,Integer,Long,Character這5種整型的包裝類也只是在對應值小於等於127時才可使用物件池,也即物件不負責建立和管理大於127的這些類的物件。
java中的常量池技術,是為了方便快捷地建立某些物件而出現的,當需要一個物件時,就可以從池中取一個出來(如果池中沒有則建立一個),則在需要重複重複建立相等變數時節省了很多時間。常量池其實也就是一個記憶體空間,不同於使用new關鍵字建立的物件所在的堆空間。

常量池中物件和堆中的物件:

public class Test{
	
	public static void main(String[] args){
		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指向常量池中同一個記憶體空間

		   System.out.println(i3==i4);//輸出true

		//很顯然,i1,i3位於不同的記憶體空間

		System.out.println(i1==i3);//輸出false
	}
}

8種基本型別的包裝類和物件池:

public class Test{
	
	public static void main(String[] args){
		   //5種整形的包裝類Byte,Short,Integer,Long,Character的物件,

		   //在值小於127時可以使用常量池
		   Integer i1=127;
		   Integer i2=127;
		   System.out.println(i1==i2);//輸出true

		   //值大於127時,不會從常量池中取物件
		   Integer i3=128;
		   Integer i4=128;

		   System.out.println(i3==i4);//輸出false

		   //Boolean類也實現了常量池技術
		   Boolean bool1=true;
		   Boolean bool2=true;
		   System.out.println(bool1==bool2);//輸出true
		   //浮點型別的包裝類沒有實現常量池技術

		   Double d1=1.0;
		   Double d2=1.0;
		   System.out.println(d1==d2);//輸出false

		}
}

String也實現了常量池技術:

public class Test{

public static void main(String[] args){
//s1,s2分別位於堆中不同空間

String s1=new String("hello");
String s2=new String("hello");

System.out.println(s1==s2)//輸出false

//s3,s4位於池中同一空間

String s3="hello";
String s4="hello";

System.out.println(s3==s4);//輸出true

}
}

用new String()建立的字串不是常量,不能在編譯期就確定,所以new String()建立的字串不放入常量池中,他們有自己的地址空間。
String 物件(記憶體)的不變性機制會使修改String字串時,產生大量的物件,因為每次改變字串,都會生成一個新的String。 java 為了更有效的使用記憶體,常量池在編譯期遇見String 字串時,它會檢查該池內是否已經存在相同的String 字串,如果找到,就把新變數的引用指向現有的字串物件,不建立任何新的String 常量物件,沒找到再建立新的。所以對一個字串物件的任何修改,都會產生一個新的字串物件,原來的依然存在,等待垃圾回收。


最後

在JDK5.0之前是不允許直接將基本資料型別的資料直接賦值給其對應地包裝類的,如:Integer i = 5; 

但是在JDK5.0中支援這種寫法,因為編譯器會自動將上面的程式碼轉換成如下程式碼:Integer i=Integer.valueOf(5);  

這就是Java的裝箱.JDK5.0也提供了自動拆箱. Integer i =5;  int j = i;  


相關文章