本文目錄一覽:
- 1、Java運行時常量池是什麼?
- 2、什麼叫做字元串常量池
- 3、java中,,常量池幹嘛的???他和堆內存棧內存有啥聯繫區別呢
- 4、.equals的問題
- 5、java方法區中包含哪些內容,常量池中包含哪些內容
- 6、什麼是JVM 運行時常量池
Java運行時常量池是什麼?
在class文件中,「常量池」是最複雜也最值得關注的內容。
Java是一種動態連接的語言,常量池的作用非常重要,常量池中除了包含代碼中所定義的各種基本類型(如int、long等等)和對象型(如String及數組)的常量值還,還包含一些以文本形式出現的符號引用,比如:
類和介面的全限定名;
欄位的名稱和描述符;
方法和名稱和描述符。
在C語言中,如果一個程序要調用其它庫中的函數,在連接時,該函數在庫中的位置(即相對於庫文件開頭的偏移量)會被寫在程序中,在運行時,直接去這個地址調用函數;
而在Java語言中不是這樣,一切都是動態的。編譯時,如果發現對其它類方法的調用或者對其它類欄位的引用的話,記錄進class文件中的,只能是一個文本形式的符號引用,在連接過程中,虛擬機根據這個文本信息去查找對應的方法或欄位。
所以,與Java語言中的所謂「常量」不同,class文件中的「常量」內容很非富,這些常量集中在class中的一個區域存放,一個緊接著一個,這裡就稱為「常量池」。
java中的常量池技術,是為了方便快捷地創建某些對象而出現的,當需要一個對象時,就可以從池中取一個出來(如果池中沒有則創建一個),則在需要重複重複創建相等變數時節省了很多時間。常量池其實也就是一個內存空間,不同於使用new關鍵字創建的對象所在的堆空間。本文只從java使用者的角度來探討java常量池技術,並不涉及常量池的原理及實現方法。個人認為,如果是真的專註java,就必須對這些細節方面有一定的了解。但知道它的原理和具體的實現方法則不是必須的。
常量池中對象和堆中的對象
[java] view plain copy
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指向常量池中同一個內存空間
System.out.println(i3==i4);//輸出true
//很顯然,i1,i3位於不同的內存空間
System.out.println(i1==i3);//輸出false
}
8種基本類型的包裝類和對象池
java中基本類型的包裝類的大部分都實現了常量池技術,這些類是Byte,Short,Integer,Long,Character,Boolean,另外兩種浮點數類型的包裝類則沒有實現。另外Byte,Short,Integer,Long,Character這5種整型的包裝類也只是在對應值小於等於127時才可使用對象池,也即對象不負責創建和管理大於127的這些類的對象。以下是一些對應的測試代碼:
[java] view plain copy
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也實現了常量池技術
String類也是java中用得多的類,同樣為了創建String對象的方便,也實現了常量池的技術,測試代碼如下:
[java] view plain copy
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
}
}
最後
細節決定成敗,寫代碼更是如此。
在JDK5.0之前是不允許直接將基本數據類型的數據直接賦值給其對應地包裝類的,如:Integer i = 5;
但是在JDK5.0中支持這種寫法,因為編譯器會自動將上面的代碼轉換成如下代碼:Integer i=Integer.valueOf(5);
這就是Java的裝箱.JDK5.0也提供了自動拆箱. Integer i =5; int j = i;
Integer的封裝:
[java] view plain copy
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);
}
}
由於cache[]在IntegerCache類中是靜態數組,也就是只需要初始化一次,即static{……}部分,所以,如果Integer對象初始化時是-128~127的範圍,就不需要再重新定義申請空間,都是同一個對象—在IntegerCache.cache中,這樣可以在一定程度上提高效率。
什麼叫做字元串常量池
可以理解為內存裡面專門為string類型變數開闢的一片區域
譬如String a = “abc”; 當你定義這樣一個變數的時候,java此時先會去常量池尋找有沒有”abc”這樣的字元串,如果有,直接把內存地址交給a, 否則就生成一個”abc”的字元串
當下一個String b = “abc”;的時候,發現常量池已經有”abc”了,此時JVM不會再次生成”abc”,而是直接交給”abc”引用給b, 所以此時你會發現a == b
java中,,常量池幹嘛的???他和堆內存棧內存有啥聯繫區別呢
Java中所有局部變數和對象的引用都是存儲在棧內存中的,int a = 1;它是局部變數肯定是在棧內存,但是它與常量池沒有關係;
常量池是堆內存中的一部分,專門用來存儲字元串常量;所以String a=”abc”中,引用a是存儲在棧內存的,指向常量池中的”abc”;
但是如果是String a = new String(“abc”);就又不一樣了,對於通過構造函數得到的”abc”字元串對象,引用a還是在棧內存,但是”abc”不會存到字元串常量池中,而是在常量池之外的其他堆內存中再生成一個”abc”,由於這個對象與原先常量池中的”abc”對象是equal關係,它們之間會建立起一種聯繫;
.equals的問題
==測試的是兩個引用是否指向同一個對象.
a指向的對象在堆中,”abc”在字元串常量池中,所以用==返回false
要想測試兩個字元串是否相等要用equals方法
jvm運行的時候,將內存分為兩個部分,一部分是堆,一部分是棧,椎中存放的
的是創建的對象,棧中存放的是方法調用過程中的變數或引用.
你後邊的例子就要關係到字元串常量池了
(java字元串對象內存實現的時候
,在堆中又開闢了一塊很小的內存,稱之為字元串常量池,專門用來存放特定的對象)
用s[1][3]=”abc”;這種方法創建字元串對象就會去字元串常量池中找看有沒有和”abc”相同的字元串對象,有就讓引用指向它,如果沒有就會在字元串常量池中創建一個字元串對象,然後引用指向創建的對象.
s[1][3]指向的是字元串常量池中的對象.而()中的”abc”返回的是字元串常量池中和s[1][3]指向的同一個對象的引用
所以你後邊的例子指向的是同一個對象,所以返回true;
如果用new創建字元串對象,和上邊的方式是不一樣的.
new創建對象會先在堆中(不是字元串常量池)中創建一個字元串對象,並將引用指向該對象,然後去字元串常量池中看是是否有和該對象相同的對象,若有就將new創建的對象和字元串常量池中的對象聯繫起來,若沒有就在字元串常量池中創建一個對象,並將該對象和new創建的對象聯聯繫起來.
如
String a=”abc”;
String b=”abc”;
String c=new(“abc”);
a==b 為true;
a==c 為false;
java方法區中包含哪些內容,常量池中包含哪些內容
方法區里存儲著class文件的信息和動態常量池,class文件的信息包括類信息和靜態常量池。可以將類的信息是對class文件內容的一個框架,裡面具體的內容通過常量池來存儲。
動態常量池裡的內容除了是靜態常量池裡的內容外,還將靜態常量池裡的符號引用轉變為直接引用,而且動態常量池裡的內容是能動態添加的。例如調用String的intern方法就能將string的值添加到String常量池中,這裡String常量池是包含在動態常量池裡的,但在jdk1.8後,將String常量池放到了堆中。
什麼是JVM 運行時常量池
運行時常量池 vs 常量池
要弄清楚方法區,需要理解清楚ClassFile,因為載入內的信息都在方法區。
要弄清楚方法區的運行時常量池,需要理解清楚ClassFile中的常量池。
一個Java源文件中的類、介面,編譯後產生一個位元組碼文件。而Java中的位元組碼需要數據支持,通常這種數據會很大以至於不能直接存在位元組碼文件,換另一種方式,可以存在常量池,這個位元組碼包含了指向常量池的引用。在動態鏈接的時候會用到運行時常量池。
棧幀
[圖片上傳失敗…(image-b8ec8b-1600702650468)]
動態鏈接
Javad 方法區究竟存了什麼???
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/231974.html