java中有幾種不同的常量池,以下的內容是對java中幾種常量池的介紹,其中最常見的就是字串常量池,
(1)class常量池
在Java中,Java類被編譯后就會形成一份class檔案;class檔案中除了包含類的版本、欄位、方法、介面等描述資訊外,還有一項資訊就是常量池,用于存放編譯器生成的各種字面量和符號參考,每個class檔案都有一個class常量池,
其中字面量包括:1.文本字串 2.八種基本型別的值 3.被宣告為final的常量等;
符號參考包括:1.類和方法的全限定名 2.欄位的名稱和描述符 3.方法的名稱和描述符,
(2)運行時常量池
運行時常量池存在于記憶體中,也就是class常量池被加載到記憶體之后的版本,是方法區的一部分(JDK1.8 運行時常量池在元空間,元空間也是方法區的一種實作),不同之處是:它的字面量可以動態的添加(String類的intern()),符號參考可以被決議為直接參考,
JVM在執行某個類的時候,必須經過加載、連接、初始化,而連接又包括驗證、準備、決議三個階段,而當類加載到記憶體中后,jvm就會將class常量池中的內容存放到運行時常量池中,這里所說的常量包括:基本型別包裝類(包裝類不管理浮點型,整形只會管理-128到127)和字串型別(即通過String.intern()方法可以強制將String放入常量池),運行時常量池是每個類私有的,在決議階段,會把符號參考替換為直接參考,
(3)基本型別包裝類常量池
Java 基本型別的包裝類的大部分都實作了常量池技術,Byte,Short,Integer,Long 這 4 種包裝類默認創建了數值 [-128,127] 的相應型別的快取資料,Character創建了數值在[0,127]范圍的快取資料,Boolean直接回傳True或False,如果超出對應范圍就會去創建新的物件,兩種浮點數型別的包裝類Float,Double并沒有實作常量池技術,
Integer 快取原始碼:
/**
*此方法將始終快取-128 到 127(包括端點)范圍內的值,并可以快取此范圍之外的其他值,
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
}
舉個栗子:
Integer i1 = 40;
Integer i2 = 40;
Integer i3 = 0;
Integer i4 = new Integer(40);
Integer i5 = new Integer(40);
Integer i6 = new Integer(0);
System.out.println("i1=i2 " + (i1 == i2));
System.out.println("i1=i2+i3 " + (i1 == i2 + i3));
System.out.println("i1=i4 " + (i1 == i4));
System.out.println("i4=i5 " + (i4 == i5));
System.out.println("i4=i5+i6 " + (i4 == i5 + i6));
System.out.println("40=i5+i6 " + (40 == i5 + i6));
結果:
i1=i2 true
i1=i2+i3 true
i1=i4 false
i4=i5 false
i4=i5+i6 true
40=i5+i6 true
解釋:1-4陳述句結果應該很顯然,因為Integer i1=40 這一行代碼會發生裝箱,也就是說這行代碼等價于 Integer i1=Integer.valueOf(40) ,Integer.valueOf() 方法基于減少物件創建次數和節省記憶體的考慮,快取了[-128,127]之間的數字,如果在此數字范圍內直接回傳快取中的物件,在此之外,直接new出來,顯然40在常量池的快取[-128,127]范圍內;因此,i1 直接使用的是常量池中的物件,而Integer i1 = new Integer(40) 會直接創建新的物件;陳述句 i4 == i5 + i6,因為+這個運算子不適用于 Integer 物件,首先 i5 和 i6 進行自動拆箱操作,進行數值相加,即 i4 == 40,然后 Integer 物件無法與數值進行直接比較,所以 i4 自動拆箱轉為 int 值 40,最終這條陳述句轉為 40 == 40 進行數值比較,所以結果為true,第六條陳述句同理,
額外說明:所有整型包裝類物件之間值的比較,全部使用 equals 方法比較,
對于Integer var = ?在-128至127之間的賦值,Integer物件是在 IntegerCache.cache產生,會復用已有物件,這個區間內的Integer值可以直接使用==進行判斷,但是這個區間之外的所有資料,都會在堆上產生,并不會復用已有物件,推薦使用equals方法進行判斷,
(4)字串常量池
在JDK1.6及之前版本,字串常量池存放在方法區中的,在JDK1.7版本以后,字串常量池被移到了堆中了,
HotSpot VM里,記錄interned string的一個全域表叫做StringTable,它本質上就是個HashSet<String>;這個StringTable在每個HotSpot VM的實體只有一份,被所有的類共享,
注意:它只存盤對java.lang.String實體的參考,而不存盤String物件的內容
字串常量池和上面的基本型別包裝類常量池有些不同,字串常量池中沒有事先快取一些資料,而是如果要創建的字串在常量池記憶體在就回傳物件的參考,如果不存在就創建一個放在常量池中;
在Java中,有兩種創建字串物件的方法,一種是字面量直接創建,另一種是new一個String物件,這兩種方法創建字串物件的程序會不一樣;
(1)String str = "abc";
(2)String str = new String("abc");
如果是第一種方式創建物件,因為是字面量直接創建,所以在編譯的時候是確定的,如果該字串不在常量池中會將該字串放入常量池中并回傳字串物件的參考,如果在常量池中直接回傳字串物件的參考,如果是第二種方式創建物件,因為要創建String型別的物件,String物件是在運行時才加載到記憶體的堆中的,屬于運行時創建,所以要先在堆中創建一個String物件,再去常量池中尋找是否有相同的字串,如果有就回傳堆中Sring物件的參考,如果沒有則在將該字串加入常量池中,
舉個栗子:
比較下列兩種創建字串的方法:
String str1 = new String("abc");
String str2 = "abc";
答案:第一種是用new()來新建物件的,它會在存放于堆中,每呼叫一次就會創建一個新的物件, 運行時期創建 ,
第二種是先在堆疊中創建一個對String類的物件參考變數str2,然后通過符號參考去字串常量池里找有沒有”abc”,如果沒有,則將”abc”存放進字串常量池,并令str2指向”abc”,如果已經有”abc” 則直接令str2指向“abc”,“abc”存于常量池在 編譯期間完成 ,
String s = new String("abc")
這條陳述句創建了幾個物件?
答案:共2個,第一個物件是”abc”字串存盤在常量池中,第二個物件在Java Heap中的 String 物件,這里不要混淆了s是放在堆疊里面的指向了Heap堆中的String物件,
String s1 = new String("s1") ;
String s1 = new String("s1") ;
上面一共創建了幾個物件?
答案:答案:3個 ,編譯期常量池中創建1個,運行期堆中創建2個.(用new創建的每new一次就在堆上創建一個物件,用引號創建的如果在常量池中已有就直接指向,不用創建)
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/298564.html
標籤:其他
上一篇:python 學習 (三)
