聲明 來源於:http://hi.baidu.com/boywell/blog/item/a069bccbc45e7c4cf31fe758.html
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 4.4章節.
關於String類的說明
1.String使用private final char value[]來實現字符串的存儲,也就是說String對象創建之後,就不能
再修改此對象中存儲的字符串內容,就是因為如此,才說String類型是不可變的(immutable).
2.String類有一個特殊的創建方法,就是使用""雙引號來創建.例如new String("i am")實際創建了2個
String對象,一個是"i am"通過""雙引號創建的,另一個是通過new創建的.只不過他們創建的時期不同,
一個是編譯期,一個是運行期!
3.java對String類型重載了+操作符,可以直接使用+對兩個字符串進行連接.
4.運行期調用String類的intern()方法可以向String Pool中動態添加對象.
String的創建方法一般有如下幾種
1.直接使用""引號創建.
2.使用new String()創建.
3.使用new String("someString")創建以及其他的一些重載構造函數創建.
4.使用重載的字符串連接操作符+創建.
例1
/*
* "sss111"是編譯期常量,編譯時已經能確定它的值,在編譯
* 好的class文件中它已經在String Pool中了,此語句會在
* String Pool中查找等於"sss111"的字符串(用equals(Object)方法確定),
* 如果存在就把引用返回,付值給s1.不存在就會創建一個"sss111"放在
* String Pool中,然後把引用返回,付值給s1.
*
*/
String s1 = "sss111";
//此語句同上
String s2 = "sss111";
/*
* 由於String Pool只會維護一個值相同的String對象
* 上面2句得到的引用是String Pool中同一個對象,所以
* 他們引用相等
*/
System.out.println(s1 == s2); //結果為true
例2
/*
* 在java中,使用new關鍵字會創建一個新對象,在本例中,不管在
* String Pool中是否已經有值相同的對象,都會創建了一個新的
* String對象存儲在heap中,然後把引用返回賦給s1.
* 本例中使用了String的public String(String original)構造函數.
*/
String s1 = new String("sss111");
/*
* 此句會按照例1中所述在String Pool中查找
*/
String s2 = "sss111";
/*
* 由於s1是new出的新對象,存儲在heap中,s2指向的對象
* 存儲在String Pool中,他們肯定不是同一個對象,只是
* 存儲的字符串值相同,所以返回false.
*/
System.out.println(s1 == s2); //結果為false
例3
String s1 = new String("sss111");
/*
* 當調用intern方法時,如果String Pool中已經包含一個等於此String對象
* 的字符串(用 equals(Object)方法確定),則返回池中的字符串.否則,將此
* String對象添加到池中,並返回此String對象在String Pool中的引用.
*/
s1 = s1.intern();
String s2 = "sss111";
/*
* 由於執行了s1 = s1.intern(),會使s1指向String Pool中值為"sss111"
* 的字符串對象,s2也指向了同樣的對象,所以結果為true
*/
System.out.println(s1 == s2);
例4
String s1 = new String("111");
String s2 = "sss111";
/*
* 由於進行連接的2個字符串都是常量,編譯期就能確定連接後的值了,
* 編譯器會進行優化直接把他們表示成"sss111"存儲到String Pool中,
* 由於上邊的s2="sss111"已經在String Pool中加入了"sss111",
* 此句會把s3指向和s2相同的對象,所以他們引用相同.此時"sss"和"111"
* 兩個常量不會再創建.
*/
String s3 = "sss" + "111";
/*
* 由於s1是個變量,在編譯期不能確定它的值是多少,所以
* 會在執行的時候創建一個新的String對象存儲到heap中,
* 然後賦值給s4.
*/
String s4 = "sss" + s1;
System.out.println(s2 == s3); //true
System.out.println(s2 == s4); //false
System.out.println(s2 == s4.intern()); //true
例5
這個是The Java Language Specification中3.10.5節的例子,有了上面的說明,這個應該不難理解了
package testPackage;
class Test {
public static void main(String[] args) {
String hello = "Hello", lo = "lo";
System.out.print((hello == "Hello") + " ");
System.out.print((Other.hello == hello) + " ");
System.out.print((other.Other.hello == hello) + " ");
System.out.print((hello == ("Hel"+"lo")) + " ");
System.out.print((hello == ("Hel"+lo)) + " "); //lo在runtime會創建一個新的對象
System.out.println(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,請自行分析!
結果上面分析,總結如下:
1.單獨使用""引號創建的字符串都是常量,編譯期就已經確定存儲到String Pool中.
2.使用new String("")創建的對象會存儲到heap中,是運行期新創建的.
3.使用只包含常量的字符串連接符如"aa" + "aa"創建的也是常量,編譯期就能確定,已經確定存儲到String Pool中.(編譯時會直接優化成"aaaa",如果String Pool 中沒有"aaaa",就用""號創建一個String,直接放到Pool中,比如:String t = "a"+ "b" +"c"; 會優化成"abc",然後放入Pool中;又比如String s = "x"+"y"+ref;在編譯時有部分的優化:"xy",而ref + "x" +"y"就不會有部分的優化,"+"從左到右執行,ref是變量,編譯時期無法確定)
4.使用包含變量的字符串連接符如"aa" + s1創建的對象是運行期才創建的,存儲在heap中.
(根據java api文檔中String類所講,會在內部使用stringbuffer及其append方法來實現連接,然後執行toString(),這樣就會在運行時又創建一個新對象)
還有幾個經常考的面試題:
1.
String s1 = new String("s1") ;
String s2 = new String("s1") ;
上面創建了幾個String對象?
答案:3個 ,編譯期Constant Pool中創建1個,運行期heap中創建2個.
發現有空還要看看基礎了.+.jvm
沒有留言:
張貼留言