先看下代码 public class JVMDemo { public static void main(String[] args) { String s1 = "ab"; //new String() - jdk1.8后永远指向常量池中的引用(没有就在常量池中创建,不会把s2复制到常量池(区别jdk1.6))
public class JVMDemo {
public static void main(String[] args) {
String s1 = "ab";
//new String() -> jdk1.8后永远指向常量池中的引用(没有就在常量池中创建,不会把s2复制到常量池(区别jdk1.6))
String s2 = new String("ab");
String s3 = new String("a") + new String("b");
String s4 = "a";
String s5 = "b";
String s6 = s4 + s5;
String s7 = "a" + "b";
String s8 = s4 + "b";
System.out.println(s1 == s2);//false
System.out.println(s1 == s3);//false
System.out.println(s1 == s6);//false
System.out.println(s1 == s7);//true
System.out.println(s1 == s8);//false
System.out.println(s2 == s6);//false
System.out.println(s2 == s7);//false
System.out.println(s2 == s8);//false
System.out.println(s2.intern() == s3.intern());//true
System.out.println(s2.intern() == s6.intern());//true
System.out.println(s2.intern() == s8.intern());//true
System.out.println(s2.intern() == s1);//true
System.out.println(s2.intern() == s7);//true
}
}
1、s1为字符串常量,取自常量池中,所有取自常量池中相等的值最终地址都相等。(在jdk1.7之后常量池放在了堆中)
所有字面量字符串均存放在常量池中,一旦有变量的加入,则会在堆中创建对象。
2、String s8 = s4 + "b"
中的+
到底做了什么
new StringBuilder(s4).append("b").toString()
String.intern() --> 返回一个字符串,内容与此字符串相同,若常量池里有相同字符串则返回池中字符串的引用,若没有则将其复制一份添加进池中再返回池中字符串的引用。
public class JVMDemo {
public static void main(String[] args) {
String s3 = new String("a") + new String("b");
s3.intern();
String s1 = "ab";
String s2 = new String("cd");
s2.intern();
String s4 = "cd";
System.out.println(s1 == s3);//true
System.out.println(s2 == s4);//false
}
}
jdk1.6中:字符串常量池是在方法区中,方法区的实现方式是和堆一样的(方便垃圾回收),存放的位置在堆空间的永久代中
永久代中的对象和堆中其他空间的对象肯定是不等的。
jdk1.8中:字符串常量池移到了堆中,方法区中其他内容,比如class信息、运行时常量池还是在方法区中,方法区的存放位置还是在堆空间的永久代中.字符串常量池已经移到了堆中,那么使用intern()方法时,如果常量池没有,就会直接返回堆中对象,因为都在一个地方嘛,所以不需要再重新创建,这和jdk1.8是不一样的,要特别注意!
4、String s2 = new String("ab");
做了什么
常量池中无"ab" : new String("ab") 会在堆和常量池中都创建对象,因此创建了两个对象。两个对象地址不同。
常量池中有"ab" : new String("ab") 会在堆中都创建对象,因此创建了一个对象。该对象与常量池中对象地址不同。