Constant Pool常量池的概念:
在講到String的一些特殊情況時總會提到String Pool或者Constant Pool但是我想很多人都不太明白Constant Pool到底是個怎麼樣的東西運行的時候存儲在哪裡所以在這裡先說一下Constant Pool的內容
String Pool是對應於在Constant Pool中存儲String常量的區域習慣稱為String Pool也有人稱為String Constant Pool好像沒有正式的命名
在java編譯好的class文件中有個區域稱為Constant Pool他是一個由數組組成的表類型為cp_info constant_pool[]用來存儲程序中使用的各種常量包括Class/String/Integer等各種基本Java數據類型詳情參見The Java Virtual Machine Specification 章節
對於Constant Pool表的基本通用結構為:
cp_info {
u tag;
u info[];
}
tag是一個數字用來表示存儲的常量的類型例如表示String類型表示Long類型info[]根據
類型碼tag的不同會發生相應變化
對於String類型表的結構為:
CONSTANT_String_info {
u tag;
u string_index;
}
tag固定為string_index是字符串內容信息類型為:
CONSTANT_Utf_info {
u tag;
u length;
u bytes[length];
}
tag固定為length為字符串的長度bytes[length]為字符串的內容
(以下代碼在jdk中編譯)
為了詳細理解Constant Pool的結構我們參看一些代碼:
String s = sss;
String s = sss;
Systemoutprintln(s + + s);
由於sss和sss都是字符串常量在編譯期就已經創建好了存儲在class文件中
在編譯後的class文件中會存在這個常量的對應表示:
; sss
; sss
根據上面說的String常量結構我們分析一下
開始的為CONSTANT_String_info結構中的tag而應該是它的相對引用為CONSTANT_Utf_info的tag為對應字符串的長度 為字符串對應的編碼接著分析會發現後面的是對應sss的存儲結構
經過上面分析我們知道了和是兩個字符串的相對引用就可以修改class文件來修改打印的內容把class文件中的 E C D改成 E C D程序就會輸出sss sss而不是和原程序一樣輸出sss sss因為我們把對sss的相對引用改成了對sss的相對引用
public class Test {
public static void main(String[] args) {
String s = sss;
String s = sss;
}
}
在上面程序中存在個相同的常量sss對於n個值相同的String常量在Constant Pool中只會創建一個所以在編譯好的class文件中我們只能找到一個對sss的表示:
abh: ; sss
在程序執行的時候Constant Pool會儲存在Method Area而不是heap中
另外對於內容為空的字符串常量會創建一個長度為內容為空的字符串放到Constant Pool中
而且Constant Pool在運行期是可以動態擴展的
關於String類的說明
String使用private final char value[]來實現字符串的存儲也就是說String對象創建之後就不能再修改此對象中存儲的字符串內容就是因為如此才說String類型是不可變的(immutable)
String類有一個特殊的創建方法就是使用雙引號來創建例如new String(i am)實際創建了個
String對象一個是i am通過雙引號創建的另一個是通過new創建的只不過他們創建的時期不同
一個是編譯期一個是運行期!
java對String類型重載了+操作符可以直接使用+對兩個字符串進行連接
運行期調用String類的intern()方法可以向String Pool中動態添加對象
String的創建方法一般有如下幾種
直接使用引號創建
使用new String()創建
使用new String(someString)創建以及其他的一些重載構造函數創建
使用重載的字符串連接操作符+創建
例
String s = sss;
//此語句同上
String s = sss;
Systemoutprintln(s == s); //結果為true
例
String s = new String(sss);
String s = sss;
Systemoutprintln(s == s); //結果為false
String s = new String(sss);
s = sintern();
String s = sss;
Systemoutprintln(s == s);
例
String s = new String();
String s = sss;
String s = sss + ;
String s = sss + s;
Systemoutprintln(s == s); //true
Systemoutprintln(s == s); //false
Systemoutprintln(s == sintern()); //true
例
這個是The Java Language Specification中節的例子有了上面的說明這個應該不難理解了
package testPackage;
class Test {
public static void main(String[] args) {
String hello = Hello lo = lo;
Systemoutprint((hello == Hello) + );
Systemoutprint((Otherhello == hello) + );
Systemoutprint((otherOtherhello == hello) + );
Systemoutprint((hello == (Hel+lo)) + );
Systemoutprint((hello == (Hel+lo)) + );
Systemoutprintln(hello == (Hel+lo)intern());
}
}
class Other { static String hello = Hello; }
package other;
public class Other { static String hello = Hello; }
輸出結果為true true true true false true請自行分析!
結果上面分析總結如下:
單獨使用引號創建的字符串都是常量編譯期就已經確定存儲到String Pool中
使用new String()創建的對象會存儲到heap中是運行期新創建的
使用只包含常量的字符串連接符如aa + aa創建的也是常量編譯期就能確定已經確定存儲到String Pool中
使用包含變量的字符串連接符如aa + s創建的對象是運行期才創建的存儲在heap中
使用aa + s以及new String(aa + s)形式創建的對象是否加入到String Pool中我不太確定可能是必須調用intern()方法才會加入希望高手能回答!
還有幾個經常考的面試題:
String s = new String(s) ;
String s = new String(s) ;
上面創建了幾個String對象?
答案:個 編譯期Constant Pool中創建個運行期heap中創建個
String s = s;
String s = s;
s = s;
s指向的對象中的字符串是什麼?
答案: s
From:http://tw.wingwit.com/Article/program/Java/ky/201311/28198.html