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 
相关文章
相关标签/搜索