在Java语言中,String类型变量直接赋值和使用new方式新建String对象是完全不同的两种方式,产生的结果也不一样,需要了解其中的差别。
概念:
- Java常量池
类在加载完成之后,会在内存中存储类中的一些字面量(本身即是值如10,“abc”),对于字符串常量来说,Java会保证常量池中的字面量不会有多个副本,也就是说在常量池中的字符串不可能有两个字符串是相同的,但是Java代码中可能不同的变量的值是相同的,那么在编译期间,这两个变量值所在地址是相同的。而且Java在编译期间会对字符串进行一定的处理,如果一个字符串采用拼接的方式,并且拼接的内容都是字面量的话,那么会自动将字符串先拼接完再赋值,如果常量池中已经有了拼接完成之后的字面量,那么此变量的值的地址就是常量池中的完整字符串的地址。需要注意的是,String在赋值完成之后修改,是会产生新的变量的。比如:
String str = "reeves";
str = "abc";
那么实际上在常量池中存储了"reeves"和"abc"两个字面值,在字符串变量赋予新的值的时候并不会改变原先存储的值,它会再新建一个字符串,而在栈中变量存储的值的地址是变了的。
使用:
例子1:
String str1 = "reeves";
String str2 = "reeves";
System.out.println(str1==str2); // 结果:true
例子中新建了两个变量str1和str2,值相同,在编译期间变量str1和str2值得地址都可以确定,因为两个变量的值相同,指向常量池中的地址也相同,因此使用“==”符号来判断两者值得地址是否相同时,返回的是true。
例子2:
String str1 = "reeves";
String str2 = "ree"+"ves";
System.out.println(str1==str2); // 结果:true
Java语言在编译期间,对于字符串拼接且拼接元素都是字面量的情况,会自动将拼接字符串拼接完整之后再赋值,因此 String str2 = "ree"+"ves"; 就相当于 String str2 = "reeves"; 而“reeves”字面量在常量池中存在,因此str2的引用地址和str1相同。
例子3:
String str = new String("reeves");
使用new关键字新建String对象时,会在堆中新创建一个字符串对象,值为“reeves”,同时,Java也会监测常量池中是否有“reeves”字面量,如果没有,那么在常量池中再新建一个“reeves”的字面量。
例子4:
String str1 = "reeves";
String str2 = str1.intern();
System.out.println(str1==str2); //结果:true
String的intern()方法会监测常量池中是否有该字符串的值的字面量,如果有,返回字面量的地址,如果没有,则新建字面量并返回新建字面量的地址。