String类中的intern()方法。
/** |
当一个字符串调用intern()方法时会经历 如下过程。
String s1 = “jvm”(我们直接给String变量赋值时会在常量池中分配一块空间)
String s2 = new String(“jvm”) (我们通过new的方式创建字符串的时候,我们在堆中分配空间)
1.关于 + 运算符对字符串的拼接
String s1 = “a”;
String s2 = “b”;
String s3 = “ab”;
String s4 = “a” + “b”
String s5 = s1 + s2;
对于s4由于常量池中已经存在”ab”所以s4直接指向该地址空间
对于s5由于s1和s2不是final修饰的所以会创建一个新的String对象,此时空间在堆中分配
//我们将s1和s2使用final修饰
final String s1 = “a”;
final String s2 = “b”;
String s3 = “ab”;
String s4 = “a” + “b”
String s5 = s1 + s2;
此时s5也指向了常量池中的字符串对象了,在编译期间我们由于s1和s2是常量并且是final修饰的即不可变的常量,当我们到s5时会直接将s1和s2替换成”a” + “b”
String s1 = “a” + new String(“b”);
对于s1我们现在常量池中创建一个字符串a,然后再堆中new String(“b”),最后拼接两个字符串时,我们会新创建一个对象new String(“ab”)
String s1 = new String(“a” + “b”);
对于字符串s1我们先进行”a” + “b”,编译时会进行优化”a” + “b”直接又”ab”替换,然后我们再堆中创建一个新的对象。
2.关于intern()方法
String s1 = “ab”;
String s2 = new String(“ab”);
s2.intern();
在s2.intern()时,会先去常量池中查看是否有”ab”这个字符串,发现有直接返回常量池中”ab”的地址。
String s1 = new String(“ab”);
s1.intern();
在s1调用intern()方法以后,发现常量池中不存在”ab”这个常量,在jdk1.7之前我们会直接拷贝一份”ab”放在常量池中,而在jdk1.7后我们不会直接拷贝值而是复制一份s1引用地址在常量池中,最后直接返回常量池中的地址。
最后
分析几道题巩固所学知识(题目来源菜鸟教程)
String str1 = "a";
String str2 = "b";
String str3 = "ab";
String str4 = str1 + str2;
String str5 = new String("ab");
System.out.println(str5.equals(str3)); //true
System.out.println(str5 == str3); //false
System.out.println(str5.intern() == str3); //true
System.out.println(str5.intern() == str4); //false
第一个为true,String类覆写了equals方法两个字符串引用的值相等时,就返回true
第二个为false,str3指向常量池的地址,str5指向堆中的地址
第三个为true,str5.intern()时会先去常量池中查看是否有”ab”字符串,发现有(之前str3赋值时加入到常量池的值),所有str5.intern()与str3都是指向常量池中相同的位置
第四个为false,str4会在堆创建一个新的对象
String a = new String("ab");
String b = new String("ab");
String c = "ab";
String d = "a" + "b";
String e = "b";
String f = "a" + e;
System.out.println(b.intern() == a); //false
System.out.println(b.intern() == c); //true
System.out.println(b.intern() == d); //true
System.out.println(b.intern() == f); //false
System.out.println(b.intern() == a.intern()); //true
如下图解