java,string詳解

    1. Constant Pool常量池的概念:  
    2.   
    3. 在講到String的一些特殊狀況時,總會提到String Pool或者Constant Pool,可是我想不少人都不太  
    4. 明白Constant Pool究竟是個怎麼樣的東西,運行的時候存儲在哪裏,因此在這裏先說一下Constant Pool的內容.  
    5. String Pool是對應於在Constant Pool中存儲String常量的區域.習慣稱爲String Pool,也有人稱爲  
    6. String Constant Pool.好像沒有正式的命名??   
    7.   
    8. 在java編譯好的class文件中,有個區域稱爲Constant Pool,他是一個由數組組成的表,類型  
    9. 爲cp_info constant_pool[],用來存儲程序中使用的各類常量,包括Class/String/Integer等各  
    10. 種基本Java數據類型,詳情參見The Java Virtual Machine Specification 4.4章節.  
    11.   
    12.   
    13. 對於Constant Pool,表的基本通用結構爲:  
    14. cp_info {  
    15.         u1 tag;  
    16.         u1 info[];  
    17. }  
    18.   
    19.   
    20. tag是一個數字,用來表示存儲的常量的類型,例如8表示String類型,5表示Long類型,info[]根據  
    21. 類型碼tag的不一樣會發生相應變化.  
    22.   
    23. 對於String類型,表的結構爲:  
    24. CONSTANT_String_info {  
    25.         u1 tag;  
    26.         u2 string_index;  
    27. }  
    28.   
    29. tag固定爲8,string_index是字符串內容信息,類型爲:  
    30. CONSTANT_Utf8_info {  
    31.         u1 tag;  
    32.         u2 length;  
    33.         u1 bytes[length];  
    34. }  
    35.   
    36. tag固定爲1,length爲字符串的長度,bytes[length]爲字符串的內容.  
    37.   
    38. (如下代碼在jdk6中編譯)  
    39. 爲了詳細理解Constant Pool的結構,咱們參看一些代碼:  
    40.     String s1 = "sss111";  
    41.     String s2 = "sss222";  
    42.     System.out.println(s1 + " " + s2);  
    43.   
    44. 因爲"sss111"和"sss222"都是字符串常量,在編譯期就已經建立好了存儲在class文件中.  
    45. 在編譯後的class文件中會存在這2個常量的對應表示:  
    46. 08 00 11 01 00 06 73 73 73 31 31 31 08 00 13 01 ; ......sss111....  
    47. 00 06 73 73 73 32 32 32                         ; ..sss222  
    48.   
    49. 根據上面說的String常量結構,咱們分析一下  
    50. 開始的08爲CONSTANT_String_info結構中的tag,而11應該是它的相對引用,01爲  
    51. CONSTANT_Utf8_info的tag,06爲對應字符串的長度,73 73 73 31 31 31爲字符串對  
    52. 應的編碼,接着分析,會發現後面的是對應"sss222"的存儲結構.  
    53.   
    54.   
    55. 通過上面分析,咱們知道了11和13是兩個字符串的相對引用,就能夠修改class文件  
    56. 來修改打印的內容,把class文件中的  
    57. 00 6E 00 04 00 03 00 00 00 24 12 10 4C 12 12 4D  
    58. 改爲  
    59. 00 6E 00 04 00 03 00 00 00 24 12 10 4C 12 10 4D  
    60. 程序就會輸出sss111 sss111,而不是和原程序同樣輸出sss111 sss222,由於我  
    61. 們把對"sss222"的相對引用12改爲了對"sss111"的相對引用10.  
    62.   
    63.   
    64. ------------分割線  
    65. public class Test {  
    66.     public static void main(String[] args) {  
    67.         String s1 = "sss111";  
    68.         String s2 = "sss111";  
    69.     }  
    70. }  
    71.   
    72. 在上面程序中存在2個相同的常量"sss111",對於n個值相同的String常量,在Constant Pool中  
    73. 只會建立一個,因此在編譯好的class文件中,咱們只能找到一個對"sss111"的表示:  
    74. 000000abh: 08 00 11 01 00 06 73 73 73 31 31 31             ; ......sss111  
    75.   
    76.   
    77. 在程序執行的時候,Constant Pool會儲存在Method Area,而不是heap中.  
    78.   
    79. 另外,對於""內容爲空的字符串常量,會建立一個長度爲0,內容爲空的字符串放到Constant Pool中,  
    80. 並且Constant Pool在運行期是能夠動態擴展的.  
    81.   
    82.   
    83. 關於String類的說明  
    84. 1.String使用private final char value[]來實現字符串的存儲,也就是說String對象建立以後,就不能  
    85. 再修改此對象中存儲的字符串內容,就是由於如此,才說String類型是不可變的(immutable).  
    86.   
    87. 2.String類有一個特殊的建立方法,就是使用""雙引號來建立.例如new String("i am")實際建立了2個  
    88. String對象,一個是"i am"經過""雙引號建立的,另外一個是經過new建立的.只不過他們建立的時期不一樣,  
    89. 一個是編譯期,一個是運行期!  
    90.   
    91. 3.java對String類型重載了+操做符,能夠直接使用+對兩個字符串進行鏈接.  
    92.   
    93. 4.運行期調用String類的intern()方法能夠向String Pool中動態添加對象.  
    94.   
    95. String的建立方法通常有以下幾種  
    96. 1.直接使用""引號建立.  
    97. 2.使用new String()建立.  
    98. 3.使用new String("someString")建立以及其餘的一些重載構造函數建立.  
    99. 4.使用重載的字符串鏈接操做符+建立.  
    100.   
    101. 1  
    102.     /* 
    103.     * "sss111"是編譯期常量,編譯時已經能肯定它的值,在編譯 
    104.     * 好的class文件中它已經在String Pool中了,此語句會在 
    105.     * String Pool中查找等於"sss111"的字符串(用equals(Object)方法肯定), 
    106.     * 若是存在就把引用返回,付值給s1.不存在就會建立一個"sss111"放在 
    107.     * String Pool中,而後把引用返回,付值給s1. 
    108.     *  
    109.     */  
    110.     String s1 = "sss111";   
    111.   
    112.     //此語句同上  
    113.     String s2 = "sss111";  
    114.   
    115.     /* 
    116.     * 因爲String Pool只會維護一個值相同的String對象 
    117.     * 上面2句獲得的引用是String Pool中同一個對象,因此 
    118.     * 他們引用相等 
    119.     */  
    120.     System.out.println(s1 == s2); //結果爲true  
    121.   
    122.   
    123. 2  
    124.     /* 
    125.     * 在java中,使用new關鍵字會建立一個新對象,在本例中,無論在 
    126.     * String Pool中是否已經有值相同的對象,都會建立了一個新的 
    127.     * String對象存儲在heap中,而後把引用返回賦給s1. 
    128.     * 本例中使用了String的public String(String original)構造函數. 
    129.     */  
    130.     String s1 = new String("sss111");   
    131.       
    132.     /* 
    133.      * 此句會按照例1中所述在String Pool中查找 
    134.      */  
    135.     String s2 = "sss111";  
    136.       
    137.     /* 
    138.      * 因爲s1是new出的新對象,存儲在heap中,s2指向的對象 
    139.      * 存儲在String Pool中,他們確定不是同一個對象,只是 
    140.      * 存儲的字符串值相同,因此返回false. 
    141.      */  
    142.     System.out.println(s1 == s2); //結果爲false  
    143.   
    144.   
    145. 3  
    146.     String s1 = new String("sss111");   
    147.     /* 
    148.     * 當調用intern方法時,若是String Pool中已經包含一個等於此String對象 
    149.     * 的字符串(用 equals(Object)方法肯定),則返回池中的字符串.不然,將此 
    150.     * String對象添加到池中,並返回此String對象在String Pool中的引用. 
    151.     */  
    152.     s1 = s1.intern();  
    153.       
    154.     String s2 = "sss111";  
    155.       
    156.     /* 
    157.      * 因爲執行了s1 = s1.intern(),會使s1指向String Pool中值爲"sss111" 
    158.      * 的字符串對象,s2也指向了一樣的對象,因此結果爲true 
    159.      */  
    160.     System.out.println(s1 == s2);  
    161.   
    162.   
    163. 4  
    164.     String s1 = new String("111");   
    165.     String s2 = "sss111";  
    166.       
    167.     /* 
    168.     * 因爲進行鏈接的2個字符串都是常量,編譯期就能肯定鏈接後的值了, 
    169.     * 編譯器會進行優化直接把他們表示成"sss111"存儲到String Pool中, 
    170.     * 因爲上邊的s2="sss111"已經在String Pool中加入了"sss111", 
    171.     * 此句會把s3指向和s2相同的對象,因此他們引用相同.此時仍然會建立出 
    172.     * "sss"和"111"兩個常量,存儲到String Pool中. 
    173.  
    174.     */  
    175.     String s3 = "sss" + "111";  
    176.       
    177.     /* 
    178.      * 因爲s1是個變量,在編譯期不能肯定它的值是多少,因此 
    179.      * 會在執行的時候建立一個新的String對象存儲到heap中, 
    180.      * 而後賦值給s4. 
    181.      */  
    182.     String s4 = "sss" + s1;  
    183.       
    184.     System.out.println(s2 == s3); //true  
    185.     System.out.println(s2 == s4); //false  
    186.     System.out.println(s2 == s4.intern()); //true  
    187.   
    188.   
    189. 5  
    190. 這個是The Java Language Specification中3.10.5節的例子,有了上面的說明,這個應該不難理解了  
    191.     package testPackage;  
    192.     class Test {  
    193.             public static void main(String[] args) {  
    194.                     String hello = "Hello", lo = "lo";  
    195.                     System.out.print((hello == "Hello") + " ");  
    196.                     System.out.print((Other.hello == hello) + " ");  
    197.                     System.out.print((other.Other.hello == hello) + " ");  
    198.                     System.out.print((hello == ("Hel"+"lo")) + " ");  
    199.                     System.out.print((hello == ("Hel"+lo)) + " ");  
    200.                     System.out.println(hello == ("Hel"+lo).intern());  
    201.             }  
    202.     }  
    203.     class Other { static String hello = "Hello"; }  
    204.   
    205.     package other;  
    206.     public class Other { static String hello = "Hello"; }  
    207.   
    208. 輸出結果爲true true true true false true,請自行分析!  
    209.   
    210.   
    211. 結果上面分析,總結以下:  
    212. 1.單獨使用""引號建立的字符串都是常量,編譯期就已經肯定存儲到String Pool中.  
    213. 2.使用new String("")建立的對象會存儲到heap中,是運行期新建立的.  
    214. 3.使用只包含常量的字符串鏈接符如"aa" + "aa"建立的也是常量,編譯期就能肯定,已經肯定存儲到String Pool中.  
    215. 4.使用包含變量的字符串鏈接符如"aa" + s1建立的對象是運行期才建立的,存儲在heap中.  
    216. 6.使用"aa" + s1以及new String("aa" + s1)形式建立的對象是否加入到String Pool中我不太肯定,多是必須  
    217. 調用intern()方法纔會加入,但願高手能回答 @_@  
    218.   
    219.   
    220. 還有幾個常常考的面試題:  
    221.   
    222. 1.  
    223. String s1 = new String("s1") ;  
    224. String s2 = new String("s1") ;  
    225. 上面建立了幾個String對象?  
    226. 答案:3個 ,編譯期Constant Pool中建立1個,運行期heap中建立2個.  
    227.   
    228.   
    229. 2.  
    230. String s1 = "s1";  
    231. String s2 = s1;  
    232. s2 = "s2";  
    233. s1指向的對象中的字符串是什麼?  
    234. 答案: "s1"  
    235. //=======================================================================  
    236. 綜上:  
    237. String str1 = "a";//constant pool  
    238. String str2 = "b";//constant pool  
    239. String str3 = new String("a");//heap  
    240.   
    241. System.out.println(str1 == str3);//false  
    242.   
    243. String str4 = "a"+str2;//heap  
    244. String str5 = "a"+"b";//constant pool  
    245. String str6 = new String("ab");//heap  
    246. String str7 = "ab";//constant pool  
    247. System.out.println(str4 == str5);//false  
    248. System.out.println(str4.intern() == str5);//true  
    249. System.out.println(str4 == str6);//false  
    250. System.out.println(str5 == str7);//true  
    251.   
    252. String str8 = str6;//heap  
    253. System.out.println(str6 == str8);//true  
    254.   
    255. String str9 = new String("ab");//heap  
    256. System.out.println(str6 == str9);//false 
相關文章
相關標籤/搜索