参考 尚硅谷2020最新版宋红康JVM教程更新至中篇(java虚拟机详解,jvm从入门到精通
new String(xxx) 创建几个对象?
-
String str1 = new String("ABC")会创建多少个对象?
-
一个或两个。如果常量区有ABC的值,则只在堆中创建一个对象。
-
如果常量池没有,则还会在常量池中创建”ABC"。
-
怎么得知的呢?通过查看字节码,由ldc指令得知的。
-
-
String str1 = new String("A"+"B") ; 会创建多少个对象?
-
常量池 三个 “A","B”,“AB"
-
堆 一个
new String("AB")
-
总共 4个
-
对应字节码文件
0 new #13 <java/lang/String> 3 dup 4 ldc #18 <AB> 6 invokespecial #15 <java/lang/String.<init>> 9 astore_110 return
-
-
String str2 = new String("ABC") + "ABC" ; 会创建多少个对象?
-
对象1
new StringBuilder()
-
对象2
new String("ABC")
-
对象3 常量池中的 ”ABC"
-
对象4:
builder.toString()
方法相当于new String("ABCABC")
(更加深入) -
总共4个
-
对应字节码文件
0 new #6 <java/lang/StringBuilder> 3 dup 4 invokespecial #7 <java/lang/StringBuilder.<init>> 7 new #13 <java/lang/String>10 dup11 ldc #14 <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>27 astore_128 return
-
-
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 dup 4 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; 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);//true System.out.println(s3==s5);//false System.out.println(s3==s6);//false System.out.println(s3==s7);//false System.out.println(s5==s6);//false System.out.println(s5==s7);//false System.out.println(s6==s7);//false String s8 = s6.intern(); System.out.println(s3==s8);//true}
-
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;}
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 false String 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 }
-
分析