1、生成字符串方式的区别
字符串不属于基本类型,但是可以像基本类型一样,直接通过字面量赋值,当然也可以通过new来生成一个字符串对象。不过通过字面量赋值的方式和new的方式生成字符串有本质的区别:
1.1、通过字面量赋值创建字符串时,会优先在常量池中查找是否已经存在相同的字符串,倘若已经存在,栈中的引用直接指向该字符串;
倘若不存在,则在常量池中生成一个字符串,再将栈中的引用指向该字符串。
1.2、而通过new的方式创建字符串时,就直接在堆中生成一个字符串的对象,栈中的引用指向该对象。(备注,JDK 7 以后,HotSpot 已将常量池从永久代转移到了堆中)
1.3、对于堆中的字符串对象,可以通过 intern() 方法来将字符串添加的常量池中,并返回指向该常量的引用。
2、一些骚操作
2.1、在JAVA 1.6之后,常量字符串的“+”操作,编译阶段直接会合成为一个字符串。
2.2、对于final字段,编译期直接进行了常量替换,而对于非final字段则是在运行期进行赋值处理的。
3、代码片段分析
3.1、代码段1
public class StringTest {
public static void main(String[] args) {
String str1 = "string";
String str2 = new String("string");
String str3 = str2.intern();
System.out.println(str1==str2);//#1
System.out.println(str1==str3);//#2
}
}
3.2、代码段2
public class StringTest01 {
public static void main(String[] args) {
String baseStr = "baseStr";
final String baseFinalStr = "baseStr";
String str1 = "baseStr01";
String str2 = "baseStr"+"01";
String str3 = baseStr + "01";
String str4 = baseFinalStr+"01";
String str5 = new String("baseStr01").intern();
System.out.println(str1 == str2);//#3
System.out.println(str1 == str3);//#4
System.out.println(str1 == str4);//#5
System.out.println(str1 == str5);//#6
}
}
3.3、代码段3-1
public class InternTest {
public static void main(String[] args) {
String str2 = new String("str")+new String("01");
str2.intern();
String str1 = "str01";
System.out.println(str2==str1);//#7
}
}
3.4、代码段3-2
public class InternTest01 {
public static void main(String[] args) {
String str1 = "str01";
String str2 = new String("str")+new String("01");
str2.intern();
System.out.println(str2 == str1);//#8
}
}
3.5、结果分析
结果 #1:因为str1指向的是字符串中的常量,str2是在堆中生成的对象,所以str1==str2返回false。
结果 #2:str2调用intern方法,会将str2中值(“string”)复制到常量池中,
但是常量池中已经存在该字符串(即str1指向的字符串),
所以直接返回该字符串的引用,因此str1==str2返回true。
结果#3:str1==str2 肯定会返回true,因为str1和str2都指向常量池中的同一引用地址。
结果 #4:因为str3实际上是stringBuilder.append()生成的结果,所以与str1不相等,结果返回false。
结果 #5 :因为str1和str4指向的都是常量池中的第三项,所以str1==str4返回true。
结果 #6 :因为str5和str1都指向的都是常量池中的同一个字符串,所以str1==str5返回true。
结果 #7:在第一种情况下,因为常量池中没有“str01”这个字符串,
所以会在常量池中生成一个对堆中的“str01”的引用,而在进行字面量赋值的时候,常量池中已经存在,
所以直接返回该引用即可,因此str1和str2都指向堆中的字符串,返回true。
结果 #8:调换位置以后,因为在进行字面量赋值(String str1 = "str01")的时候,常量池中不存在,
所以str1指向的常量池中的位置,而str2指向的是堆中的对象,再进行intern方法时,
对str1和str2已经没有影响了,所以返回false。