String 拼接相关问题
常见面试题
一、new String(“xxx”)到底创建几个对象?
1. String str1 = new String(“ABC”)会创建多少个对象?
- 一个或两个。如果常量区有ABC的值,则只在堆中创建一个对象。
- 如果常量池没有,则还会在常量池中创建”ABC”。
- 怎么得知的呢?通过查看字节码,由ldc指令得知的。
2. String str1 = new String(“A”+”B”) ; 会创建多少个对象?
- 常量池 三个 “A”,”B”,“AB”
- 堆 一个
new String("AB") - 总共 4个
- 对应字节码文件
java 0 new #13 <java/lang/String> //new String("AB"); 3 dup 4 ldc #18 <AB> // 常量池 "A" "B" "AB" 6 invokespecial #15 <java/lang/String.<init>> 9 astore_1 10 return
3.String str2 = new String(“ABC”) + “ABC” ; 会创建多少个对象?
- 对象1
new StringBuilder() - 对象2
new String("ABC") - 对象3 常量池中的 ”ABC”
- 对象4:
builder.toString()方法类似于new String("ABCABC")(接收的是char数组,而非“ABCABC”常量,故常量池中没有”ABCABC”) - 总共4个
- 对应字节码文件
java 0 new #6 <java/lang/StringBuilder> // new StringBuilder() 3 dup 4 invokespecial #7 <java/lang/StringBuilder.<init>> 7 new #13 <java/lang/String> // new String("ABC") 10 dup 11 ldc #14 <ABC> // 常量池 "ABC" 13 invokespecial #15 <java/lang/String.<init>> 16 invokevirtual #8 <java/lang/StringBuilder.append> 19 ldc #14 <ABC> 21 invokevirtual #8 <java/lang/StringBuilder.append> 24 invokevirtual #10 <java/lang/StringBuilder.toString> // toString() 27 astore_1 28 return
4. String str3 = new String(“A”) +new String(“B”); 会创建多少个对象?
对象1
new StringBuilder()对象2
new String("A")对象3 常量池中的“A”
对象4
new String("B")对象5 常量池中的“B”
对象6
builder.toString()方法近似于new String("AB"),强调一下,常量池里面并没有生成AB总共6个
对应字节码文件:
0 new #6 <java/lang/StringBuilder>3 dup4 invokespecial #7 <java/lang/StringBuilder.<init>>7 new #13 <java/lang/String>10 dup11 ldc #16 <A>13 invokespecial #15 <java/lang/String.<init>>16 invokevirtual #8 <java/lang/StringBuilder.append>19 new #13 <java/lang/String>22 dup23 ldc #17 <B>25 invokespecial #15 <java/lang/String.<init>>28 invokevirtual #8 <java/lang/StringBuilder.append>31 invokevirtual #10 <java/lang/StringBuilder.toString>34 astore_135 return
二、String拼接的底层是怎么做的?
知识点
- 常量与常量的拼接,结果放在常量池中,原理是编译期优化
- 常量池中不会存在相同内容的对象。
- 只要其中一个是变量,结果就放在堆中。变量拼接的原理是
StringBuilder。 - 若拼接的结果调用
intern()方法,则主动将常量池还没有的字符串对象放入池中,并返回此对象地址。 通过
StringBuilder的append()方式添加字符串的效率要远高于使用String的字符串拼接方式。- 前者只创建过一个
StringBuilder对象, 后者在每次循环中都要创建一个新的StringBuilder和String对象。 - 后者由于内存中创建了较多的
StringBuilder和String对象,内存占用更大,如果进行GC,需要花费额外的时间。
- 前者只创建过一个
代码示例
@Testpublic void test1(){String s1 = "a"+"b"+"c";String s2 = "abc";/**执行细节:常量池中创建了三个变量 "a" "ab" "abc"**/System.out.println(s1==s2);//true}@Testpublic void test2_1(){String s1 = "a";String s2 = s1 + "b";String s3 = "ab";/**执行细节:1 StringBuilder s = new StringBuilder();2 s.append("a")3 s.append("b")4. s.toString(); 类似于 new String("ab")补充 jdk5.0之后使用的是StringBuilder,以前使用StringBuffer**/System.out.println(s3==s2);}@Testpublic void test2(){String s1 = "a";String s2 = "b";String s3 = "ab";/**执行细节:1 StringBuilder s = new StringBuilder();2 s.append("a")3 s.append("b")4. s.toString(); 类似于 new String("ab")补充 jdk5.0之后使用的是StringBuilder,以前使用StringBuffer**/String s4 = s1+s2;System.out.println(s3==s4);}@Testpublic void test3(){String s1 = null; //不理解的话见下方的StringBuilder的append()方法String s2 = "b";String s3 = s1+s2;/*** 执行细节:* 1 StringBuilder s = new StringBuilder()* 2 s.append(s1)* 3 s.append("b")* 4 s.toString(); 类似于new String("nullb")*/System.out.println(s3);}@Testpublic void test4(){final String s1 = "a";final String s2 = "b";String s3 = "ab";String s4 =s1 + s2; //从字符串常量池中取的System.out.println(s3==s4);//true}@Testpublic void test(){String s1 = "hello";String s2 = "world";String s3 = "helloworld";String s4 = "hello" + "world";String s5 = s1 + "world";String s6 = "hello" + s2;String s7 = s1 + s2;System.out.println(s3==s4);//trueSystem.out.println(s3==s5);//falseSystem.out.println(s3==s6);//falseSystem.out.println(s3==s7);//falseSystem.out.println(s5==s6);//falseSystem.out.println(s5==s7);//falseSystem.out.println(s6==s7);//falseString s8 = s6.intern();System.out.println(s3==s8);//true}
- PS:
StringBuilder的append()方法:
private StringBuilder append(StringBuilder sb) {if (sb == null)return append("null");int len = sb.length();int newcount = count + len;if (newcount > value.length)expandCapacity(newcount);sb.getChars(0, len, value, count);count = newcount;return this;}
String的intern()方法
- jdk6: 执行intern()方法时,若常量池中不存在等值的字符串,JVM就会在池中创建一个等值的字符串,然后返回该字符串的引用。
- jdk7: 执行intern()方法时,若常量池中已存在该字符串,则直接返回字符串引用,否则复制该字符串的引用到常量池中并返回。
- 例子:
public static void main(String[] args) {String s = new String("1"); //创建了两个对象,一个堆中的“1”,一个常量池中的“1”s.intern(); //没有作用,因为常量池中有"1"了String s2 = "1";System.out.println(s==s2);// jdk6 false jdk8 falseString s3 = new String("1")+new String("1");//虽然创建了6个对象,但常量池中没有“11”s3.intern();//对于1.8来说,直接复制引用到常量池。对于1.6则是创建了一个新对象String s4 = "11";System.out.println(s3==s4);// jdk6 false jdk8 true}
- 分析:

