一、弄清楚String对象的特点:
不可变的(final);
二、弄清楚+号和StringBuilder:
+号操作分n种情况,但只有一种情况等同于直接字符串,那就是常量值相加,像这样:
String str = "abc" + 123; 等同于String str = "abc123";
String str = "abc" + "def"; 等同于String str = "abcdef";
指令为证:
stack=1, locals=2, args_size=0
0: ldc #64 // String abc123
2: astore_0
3: ldc #66 // String abcdef
5: astore_1
除此之外,其他的相加一律视为StringBuilder对象,例如:
int i = 1;
String str = "abc" + i;
查看这两句的指令:
0: bipush 123
2: istore_0
3: new #64 // class java/lang/StringBuilder
6: dup
7: ldc #66 // String abc
9: invokespecial #68 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
12: iload_0
13: invokevirtual #69 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
16: invokevirtual #72 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
19: astore_1
20: getstatic #43 // Field java/lang/System.out:Ljava/io/PrintStream;
可以看到,其最终是通过StringBuilder来append字符串的。
三、弄清楚常量池在不同JDK版本下的存储位置
这一点对了解intern很重要,从JDK1.7开始,常量池就不放在永久代的方法区了,而是放在了堆中。同时,常量池也能够存储引用对象(指向堆地址)。
OK,了解了上面基础知识,再开始看intern,正所谓磨刀不误砍柴工。
四、执行intern方法后的效果
1.6:如果String对象在常量池中不存在,执行后,会在常量池中额外增加String常量对象;
1.7:如果String对象在常量池中不存在,会在常量池中创建一个指针指向堆中的String对象,很明显1.7要比1.6先进,至少节省了存储空间;
举例说明
1,先来个简单的例子
String s1 = new String("abc");
System.out.println(s1 == s1.intern());
输出:false
解释:这个很简单,可以看到s1指向堆中地址,而s1.intern()则返回常量池中的地址。
1.6
1.7
2,常见题目
String s1 = new String("a") + new String("b");
s1.intern();
String s2 = "ab";
Sysout.out.println(s1== s1.intern());
Sysout.out.println(s1== s2);
Sysout.out.println(s1.intern() == s2);
解释:在1.7以后的版本中,String s1 = new String("a") + new String("b"); 这句话执行完,是不会往常量池中加常量对象ab的,只有a,b两个会加到常量池中。——这个前提要清楚。
JDK1.6,见左边图,s1.intern()后,会在常量池中加上ab对象,注意:是ab的值,不是引用;
JDK1.7,见右边图,s1.intern()后,会在常量池中加上指向堆地址的引用对象。
综上,比较==,只要看实线箭头最终指向哪里就可以了。