一、Class常量池解析
定義:Class常量池可以理解為是Class文件中的資源倉庫。
內容:Class文件中除了包含類的版本、字段、方法、接口等描述信息外, 還有一項信息就是常量池,用於存放編譯期生成的各種字面量和符號引用。
我們可以通過一個命令來查看我們字節碼文件的內容:
字面量
定義:字面量就是指由字母、數字等構成的字符串或者數值常量。
PS:字面量只可以右值出現【等號右邊的值】如:int a = 1 這里的a為左值,1為右值。在這個例子中1就是字面量。
private int compute() { int a = 1;//符號引用:a 字面量:1 int b = 2;//符號引用:b 字面量:2 String c = "有夢想的肥宅";//符號引用:c 字面量:有夢想的肥宅 return a + b; }
符號引用
符號引用是編譯原理中的概念,是相對於直接引用來說的。主要包括了以下三類常量:
- 類和接口的全限定名
- 字段的名稱和描述符
- 方法的名稱和描述符
符號引用只有到運行時被加載到內存后,這些符號才有對應的內存地址信息,這些常量池一旦被裝入內存就變成運行時常量池,也就引出了下面動態鏈接的概念。
動態鏈接:對應的符號引用在程序加載或運行時會被轉變為被加載到內存區域的代碼的直接引用。
例:compute()這個符號引用在運行時就會被轉變為compute()方法具體代碼在內存中的地址,主要通過對象頭里的類型指針去轉換直接引用。
二、字符串常量池解析
字符串常量池的設計思想
字符串的分配和其他的對象分配一樣,耗費高昂的時間與空間代價,作為最基礎的數據類型,大量頻繁的創建字符串,極大程度地影響程序的性能。JVM為了提高性能和減少內存開銷,在實例化字符串常量的時候進行了一些優化:
- 為字符串開辟一個字符串常量池,類似於緩存區
- 創建字符串常量時,首先查詢字符串常量池是否存在該字符串
- 存在該字符串,返回引用實例,不存在,實例化該字符串並放入池中
三種字符串操作(Jdk1.7 及以上版本)
直接賦值
String s = "有夢想的肥宅"; // s :指向常量池中的引用
PS:這種方式創建的字符串對象,只會在常量池中。
創建步驟:
JVM會先去常量池中通過 equals(key) 方法,判斷是否有相同的對象:
- 有,則直接返回該對象在常量池中的引用
- 沒有,則會在常量池中創建一個新對象,再返回引用
new String()方法創建
String s1 = new String("有夢想的肥宅"); // s1指向內存中的對象引用
PS:這種方式會保證字符串常量池和堆中都有這個對象,沒有就創建,最后返回堆內存中的對象引用。
創建步驟:
因為有"有夢想的肥宅"這個字面量,所以會先檢查字符串常量池中是否存在此字符串:
- 不存在,先在字符串常量池里創建一個字符串對象,再去內存中創建一個字符串對象"有夢想的肥宅"
- 存在,就直接去堆內存中創建一個字符串對象"zhuge", 最后,將內存中的引用返回
intern()方法
String s1 = new String("有夢想的肥宅"); String s2 = s1.intern(); //【intern:是一個native方法】 System.out.println(s1 == s2); //false
PS:這種方式創建的字符串對象,可能在常量池中存在,也可能不存在【常量池保存一個內存地址的引用,指向堆中對應的字符串對象】。
創建步驟:
還是會去常量池找看有沒有"有夢想的肥宅"這個字符串:
- 存在,則返回池中的字符串。
- 不存在,將intern返回的引用指向當前字符串 s1
PS:jdk1.6版本需要將 s1 復制到字符串常量池里
三、八種基本類型的包裝類和對象池
java中基本類型的包裝類的大部分都實現了常量池技術(嚴格來說對象在堆上應該叫對象池),這些類是 Byte、Short、Integer、Long、Character、Boolean,另外兩種浮點數類型的包裝類則沒有實現。
PS:Byte,Short,Integer,Long,Character這5種整型的包裝類也只是在對應值小於等於127時才可使用對象池,因為一般這種比較小的數用到的概率相對較大。
public class Test { public static void main(String[] args) { //1、5種整形的包裝類Byte,Short,Integer,Long,Character的對象,在值小於127時可以使用對象池 Integer i1 = 127; //PS:這種調用底層實際是執行的Integer.valueOf(127),里面用到了IntegerCache對象池 Integer i2 = 127; System.out.println(i1 == i2);//輸出true //2、當值大於127時,不會從對象池中取對象 Integer i3 = 128; Integer i4 = 128; System.out.println(i3 == i4);//輸出false //3、用new關鍵詞新生成對象不會使用對象池 Integer i5 = new Integer(127); Integer i6 = new Integer(127); System.out.println(i5 == i6);//輸出false //4、Boolean類也實現了對象池技術 Boolean bool1 = true; Boolean bool2 = true; System.out.println(bool1 == bool2);//輸出true //5、浮點類型的包裝類沒有實現對象池技術 Double d1 = 1.0D; Double d2 = 1.0D; System.out.println(d1 == d2);//輸出false } }