先上题目:
以下代码运行结果是( )
String s1 = "hello";
String s2 = "he";
String s3 = s2 + "llo";
System.out.println(s1 == s3);
看到使用直接赋值理所当然想到常量池,然后以为以+拼接出来的字符串当然也会放入常量池就填了True
然后惨被打脸
创建字符串对象时,一般有几种情况:
1,直接赋值:直接赋值时会先去常量池寻找,如果常量池有该字符常量,则直接让变量指向常量池引用;如果没有就将字符串放入常量池并让变量指向
2,使用new创建:会在堆内存分配空间,不会用到常量池,如果要用,就要调intern()方法去常量池"注册":
如果常量池有该字符串常量,则让对象指向常量池引用;如果没有,在jdk1.7以前,会直接将对象中的字符串拷贝到常量池中并返回引用;
如果jdk是1.7及以后版本,会直接让常量池常量指向堆内存中对象引用,相当于"挂名"
3,使用"+"拼接字符时:如果拼接的是final修饰的字符变量(final字符在编译阶段都会被替换为常量)或者常量,编译器会直接拼接字符串,并在常量池"注册"
如果不是常量或者final修饰的字符串,底层会通过StringBuilder拼接,所以引用会变;
延伸1:如果拼接字符串中出现null时,根据StringBuilder.append()方法源码可知,并不会出现NPE,而是将"null"拼进去
延伸2:如果代码中用到很多拼接字符串时,有必要考虑使用StringBuilder处理,这样避免了在底层new过多StringBuilder临时对象
intern方法:功能相当于将字符串"注册"到常量池
代码演示:
String s2 = new String("1");
s2.intern();
String s1 = "1";
System.out.println(s1 == s2);//false 在构建s2前已经用到了常量,并注册到了常量池
String s3 = s1+"1";
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);//true(jdk1.7以前为false) 注册后常量池指向了s3
System.out.println(s3.intern() == s3);//true 套娃
final String s1 = "1";
final String s2 = "2";
String s3 = s1+s2;
String s4 = new String("12");
System.out.println(s3 == s4.intern());//true 这里s3实质上相当于常量
如有不对,欢迎指正!
参考1