Java對String字符串對象的創建,管理和“+”運算符的實現

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 " );

#008000">/*
    * 當調用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 " )) + " "

style="COLOR: #000000">);
                    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個.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章