知识补充
- 在JDK6之前,字符串常量池存放在永久代;在JDK6之后,字符串常量池存放在Java堆内。
- 字符串常量池中不会存储相同内容的字符串。
- intern()方法:如果字符串常量池中没有对应的字符串的话,会先在字符串常量池中生成该字符串,然后返回对象的地址,如果存在对应的字符串,则直接返回字符串常量的地址。
通过字面量创建字符串
String s1 = "Java";
System.out.print(s1.intern()==s1);
- 执行上述语句时,如果字符串常量池中没有“Java”这个字符串,会在字符串常量池中先创建“Java”字符串对象,然后将s1指向该对象的地址;如果字符串常量池中已经存在“Java”字符串对象,则无需创建对象,直接将s1指向字符串常量池中的对象。
- s1.intern()也是指向字符串常量池中的对象,所以输出语句输出结果为true。
- 故上述语句创建0个或者1个对象。
通过new关键字创建字符串
String s2 = new String("Python");
System.out.print(s2.intern()==s2);
- 执行上述语句时,会先检查字符串常量池存不存在“Python”这个字符串,不存在则在字符串常量池中创建该字符串对象,再去Java堆中创建“Python”字符串对象,并将s2指向堆中对象的地址。
- s2.intern()也是指向字符串常量池中的对象,所以输出语句输出结果为false。
- 故上述语句创建1个或者2个对象。
字符串拼接创建字符串
字符串常量的拼接
String s3 = "Er"+"lang";
- 执行上述语句,jvm编译阶段通过编译器优化后,会把字符串常量直接合并成“Erlang”,相当于 String s3 = “Erlang” 此时若字符串常量池中没有“Erlang”则创建该字符串对象,并将s3指向该对象,若字符串常量池中已经存在“Erlang”,则不创建,直接将s3指向该对象。
- 故上述语句创建0个或者1个对象。
字符串变量的拼接
String s4 = new String("Go")+new String("lang");
- 假设字符串常量池中不存在“Go”和“lang”字符串对象。执行该语句,先去字符串常量池中创建“Go”,"lang"两个对象,然后再去Java堆中创建“Go”,“lang”两个对象,使用+号拼接时,内部创建了一个StringBuilder对象,通过append()方法拼接,最后调用StringBuilder的tostring()方法得到一个String对象”Golang“,并把它赋值给s4。
- 注意此时字符串常量池中并没有“Golang”字符串对象。
- 故上述语句创建了6个对象(字符串常量池一开始不存在字符串的情况下)。
这时的intern()方法还有点小细节
String s4 = new String("Go")+new String("lang");
s4.intern();
String s5 = "Golang";
System.out.print(s4==s5);
- 从上文得知,语句1并没有在字符串常量池中生成”Golang“字符串对象。
- 在jdk6版本,执行intern()方法,如果常量池中没有该字符串对象,则会在字符串常量池新建一个字符串对象。此时s5就会指向intern方法生成的这个常量池中的字符串对象,输出语句返回的结果为false。

2.在jdk7/8版本,字符串常量池被转移到了java堆中,执行完intern()方法后, 如果常量池中没有该字符串对象,则会把此对象在堆中的引用地址复制一份,放入字符串常量池中,并返回字符串常量池中的引用地址。此时输出语句返回的结果是true。

一些字符串”==“比较练习
String aa = "abc";
String bb = new String("abc");
String cc = "abc";
String dd = "ab";
System.out.println(aa == (dd + "c"));
System.out.println(aa == ("ab" + "c"));
System.out.println(aa == cc);
System.out.println(aa == bb);
System.out.println(aa.equals(dd + "c"));
System.out.println("------------------------");
String s1 = "coder";
String s2 = "coder";
String s3 = "coder" + s2;
String s4 = "coder" + "coder";
String s5 = s1 + s2;
String s6 = s1 + s1;
System.out.println(s4 == s5);
System.out.println(s6 == s5);
System.out.println(s3 == s5);
System.out.println(s4 == "codercoder");
System.out.println(s5 == "codercoder");
System.out.println(s3 == "codercoder");
打印字符串对象地址的方法
String s1 = "coder";
String s2 = "coder";
String s4 = "coder" + "coder";
String s5 = s1 + s2;
String s6 = s1 + s1;
System.out.println(String.class.getName() + "@" + Integer.toHexString(System.identityHashCode(s4)));
System.out.println(String.class.getName() + "@" + Integer.toHexString(System.identityHashCode(s4)));
System.out.println(String.class.getName() + "@" + Integer.toHexString(System.identityHashCode(s5)));
System.out.println(String.class.getName() + "@" + Integer.toHexString(System.identityHashCode(s6)));
System.out.println(String.class.getName() + "@" + Integer.toHexString(System.identityHashCode("codercoder")));
java.lang.String@1c4af82c
java.lang.String@379619aa
java.lang.String@cac736f
java.lang.String@1c4af82c