心血来潮,在这里探究下关于String的地址问题
public class Test {
public static void main(String[] args) throws Exception {
String s1 = "helloworld";
String s2 = new String("helloworld");
String s3 = "hello";
String s4 = "world";
String s5 = "hello" + "world"; /*两个常量相加,是静态的,结果存在常量池中*/
String s6 = s3 + "world"; /*s3为变量,是动态的,结果存放在堆中*/
System.out.println(s1 == s2); //false
System.out.println(s1 == s5); //true
System.out.println(s1 == s6); //false
System.out.println(s1 == s6.intern()); //true
System.out.println(s1 == s2.intern()); //true
System.out.println(s2 == s2.intern()); //false
}
}
- 关于上面的代码,有以下几点是必须要搞清楚的:
- s1为字符串常量,取自
常量池
中,所有取自常量池中相等的值最终地址都相等。(在jdk1.7之后常量池放在了堆中) - String s6 = s3 + "world"中的
+
到底做了什么
String s3= "hello";
StringBuilder sb = new StringBuilder(); //创建一个临时的StringBuilder对象
sb.append(s3).append("world");
String s6= sb.toString(); //s6="helloworld
String.intern()
的作用是什么
String.intern() --> 返回一个字符串,内容与此字符串相同,若常量池里有相同字符串则返回池中字符串的引用,若没有则将其复制一份添加进池中再返回池中字符串的引用。
这里那拿上面的
String s2 = new String("helloworld")
来举例:
- jdk1.6中:先判断常量池中是否有"helloworld",如果有直接返回常量池中的地址,如果没有则拷贝一份s2对象放入常量池(此时,
new String
仍指向原来堆中的地址)- jdk1.8中:先判断常量池中是否有"helloworld",如果有直接返回常量池中的地址,如果没有则将该对象放入常量池中。(此时,
new String
已经指向了常量池中的helloworld的地址)
- 什么时候会在
常量池
存储字符串对象
- 显示调用String的intern方法的时候;
- 直接声明字符串字面常量的时候,例如: String s = “helloworld”;
- 字符串直接常量相加的时候,例如: String s = “hello” + “world”; 其中的hello、world只要有任何一个不是
字符串字面常量
(“字面常量”,而不是变量名的形式),都不会在常量池生成"helloworld". 且此时jvm做了优化,不会同时生成"hello"和"world"在字符串常量池中;