字符串比较
Step1
我在CDSN论坛上看到这样一个帖子,觉得挺不错的,自己在这方面也正在学,于是乎去尝试了一下,问题截图如下:
原帖链接:http://bbs.csdn.net/topics/391957440
下面是那位贴出问题的博友,在得到网友回答后写出的总结:
1: 对于 String str3 = “JavaEE” + “Android”;这条语句会在编译时期确定,如果常量池中有 “JavaEEAndroid”则会将这个字符串的地址放到str3中。
如果没有,则会在常量池中新建,然后赋值引用。
2: 对于 str1= str1 + “Android”; 在编译时并没有确定,只有在运行时才能确定值。所以会在堆中新建对象String 类型 然后赋值给str1;
这样str1 所引用的地址并不是”JavaEEAndroid”的地址。所以不相等。
Step2
我拿到这个问题后,也学着我之前看视频的老师那样,画画内部原理图,如下:
等我画完才发现,从我这样画的原理图,最终str1与str3应该是一样的地址引用值才对,可结果 str1 == str3 为 false,就说明这里str1与str3实际上是不等的。
于是又折腾了一晚上,等到第二天才算是将其弄清楚,见下图。
Step3
以下是我的见解:
下面是一个Java字符串连接的例子:
String one = "Hello";
String two = "World";
String three = one + " " + two;
编译器最终会把上面的代码编译为类似下面的代码:
String one = "Hello";
String two = " World";
String three = new StringBuilder(one).append(two).toString();
此代码实际上创建了两个对象:一个StringBuilder实例,然后从toString()方法返回一个新的String实例。
@Override
public String toString() {
// Create a copy, don‘t share the array
return new String(value, 0, count);
}
此处最终得到的是new 出来的一个String实例,进而会在堆中开辟一个空间,继而String 的引用result 会指向这个新开辟的起始地址
仍需注意的是,上例中StringBuilder()和append()传进去的参数是String类型的,当定义的String类型被如下final修饰的时候,str1直接就是常量了:
@Test
public void Test01(){
final String str1 = "JavaEE";
String str = str1 + "Android";
String str3 = "JavaEEAndroid";
System.out.println(str3 == str);//true
System.out.println(str3.equals(str));//true
}
此处str3 与 str1就不会像第一例那样调用StringBuilder进行字符串的串联,而是直接相连接存入到常量池中
Step4
总结:这里理解的重点是,在字符串用’+‘号串联时,什么时候用” new StringBuilder(one).append(two).toString();”的形式,什么时候直接相连,
关键看’+‘号两侧的字符串是变量还是常量,若都为String类型的引用变量,则必定是调用StringBuilder方法,若存在声明为final的常量,则直接相连,
补充一点:前者是在堆中重新开辟一个存储空间,用来存储指向常量池中该字符串的首地址;而后者是直接在堆常量池中创建串联后的新字符串(若池中
原先就存在了,就直接用原来的)