由JVM内存管理可知,在方法区中有一块独立的区域,被称之为运行时常量池,在虚拟机启动时,JVM会自动创建,有9种类型的常量池,8大基本类型与String类型。
常量池是存放数据字面量的,比如“abc”,1,5.0,'c',true等等(注:String类的具体值,都是存在常量池中,堆中存的是常量池中的引用;8大基本类型的包装类中,除了Float,Double类意外,都有实现常量池,但是,数值类型的包装类要小于127的时候才会运用常量池,大于127就直接存储于栈中了。基本数据类型的数据是直接存放于栈中的;更多信息,参见博文《Integer与int小谈》);但是,String毕竟是一个类,而8大基本类型也有其对应的包装类;类就与堆离不开关系,下面就介绍下这些类的对象是如何徘徊再堆与常量池之间的。(注:由于8大基本类型与String差不多,这里只介绍String)。
1. String对象的创建。
String对象有两种创建方式:
a.通过字面量;String str = "wangjxy";
b.通过new关键字创建;String str = new String("wangjxy");
然而,这两种之间有非常大的差异。
解释说明:第一:凡是出现"wangjxy"等形式的字符串赋值(即将该整体赋值给=号左边),都要与常量池进交互:先查询常量池
是否有该对象,若有,则直接将常量池中的该对象的引用返回给=号左边;若没有,则会先再常量池中把该对象添加
进去,然后返回该引用;如String str = "wangjxy";在编译的时候先会检查在常量池中有无"wangjxy"对象,并根据以上
规则返回相应的引用给变量str;
第二:String str = new String("wangjxy")的时候,首先,出现new关键字,肯定需要在堆中开辟相应的空间,同样,出
现了"wangjxy"字面量,也会在常量池中做相应的引用返回,因此,这时候,可能会在原先"wangjxy"对象上在堆中再创
建一个,也有可能同时在堆与常量池中各创建一个,而变量str指向的是堆中该对象的地址,堆中引用的是常量池中的
字面量;
因此:对于a方式,堆中开辟空间,变量指向的是常量池中的对象;对于b方式,变量str指向的是堆,具体该条语句产生几个对
象,由原先常量池中有无该对象决定。
可解释代码:
String str1 = "wangjxy";
String str2 = new String("wangjxy");
// str1.equals(str2);->true
// str1 == str2; ->false
2.String拼接。
String拼接也有两种方式:
a.String str = "a" + "b";
b.String str1 = "a";
String str2 = "b";
String str3 =str1 +str2;
解释说明:对于a方式,字面量拼接;由于字面量在编译器编译的时候,就能够解析到,所以在=号右面的所有字面量都会一起解
析后,拼接完毕再以一个完整的字符串存到常量池中,并返回该整个字符串在常量池中的引用;
对于b方式,字符串变量的拼接,这时候,在编译的时候,编译器无法间接去取得变量对应的具体字面量,所以,它
会在运行时候首先在堆中动态的创建一个对象,然后根据对象中str1,str2两个变量的引用去取得拼接字面量,把拼接
好的字面量放入常量池并返回该引用给堆中对象;若已经有该字面量对象 ,则直接返回该引用;但str5指向的是堆中
的对象地址,而不是常量池中的字面量。
可解释代码:
String str1 = "a" + "b";
String str2 = "ab";
//str1 == str2; ->true;
String str3 = "a";
String str4 = "b";
String str5 =str3 +str4;
//str5 == str2; -> false
3.带final字段的String拼接。 该种方式的拼接也有两种形式:
a. String s1 = "ab";
final String s2 = "b";
String s3 = "a" + s2;
b. String s1 = "ab";
String s2 = "b";
final String s3 = s2;
String s4 = "a" + s3;
解释说明: 对于a方式,由于s2被final修饰,而且等号后面跟着直接可以读取的字面量,所以s2被直接解析为"b"而且把b写入了s2
变量中,所以 "a" + s2 就相当于 "a" + "b";对于b方式,虽然s3被final修饰,但是由于等号后面的是变量不是字面量,无
法直接被编译器解析,所以同样只有等到程序执行的时候才能去读取,因此这里的s4同样会被动态分配堆空间;
String s1 = "ab";
final String s2 = "b";
String s3 = "a" + s2;
//s3 == s1;->true
String s4 = "ab";
String s5 = "b";
final String s6 = s5;
String s7 = "a" + s6;
//s7 == s4;->false
4. intern()方法
要给常量池中添加新对象,一种方法是通过字面量创建对象,第二种就是通过intern方法。
intern()方法返回String类型引用,由String对象调用,把该对象的字面量值拿去常量池查询,若原先有,则直接返回常量池中该字
面量的引用;如原先无,则添加该字面量到常量池,并返回常量池中该对象的引用;所以,intern()返回的一定是常量池中的引
用。
String s1 = "ab";
String s2 = new String("ab");
String s3 = s2.intern();
//s3 == s1;->true;