先来一段简单的代码SeeSee
public class Test0{
public static String s1="static"; // 第一句
public static void main(String[] args) {
String s1=new String("he")+new String("llo"); //第二句-hello1
s1.intern(); //将 堆中新建的对象"hello" 存入字符串常量池
String s2="hello"; //第三句-hello2
System.out.println(s1==s2);//输出 true。
}
}
类加载以后,“he” “llo” “hello” 对象实例在堆上创建,并且引用在 字符串常量池中驻留。 我们将这个 "hello"对象实例叫做 hello1.
第二句,s1是在堆上新创建的 "hello"对象实例,叫做 hello2.当执行到intern()语句时, hello2 equals hello1 都是 “hello” 。但是hello1 和 hello2在堆中的地址是不一样的,那么字符串常量池中原本存储的是hello1的地址,现在是会依旧使用hello1的地址还是 更新 为 hello2的地址?这是问题1
为了描述的方便。我们将intern()执行后,字符串常量池中的"hello"引用假设为 hello3 . hello3指向 hello1 或者hello2. 这取决于问题1的答案.
第三句,直接将s2赋值为 常量池中的 hello3所指向的字符串.
但是最终输出结果 是true 。 这是说明了 hello3所指向的是hello2.
通过结果推测:问题1的答案就是,字符串常量池更新了。
举个反例:
class Test1{
public static void main(String[] args) {
String s1=new String("he")+new String("llo");//hello1
String s2=new String("h")+new String("ello");//hello2
String s3=s1.intern();
String s4=s2.intern();
System.out.println(s1==s3);
System.out.println(s1==s4);
}
}
/*打印结果
true
true
*/
前面我们的答案是字符串常量池更新了。
为了方便 s1 的 “hello” 是 hello1 . s2 的 “hello” 是 hello2 .
hello2和hello1是不同对象具有不同地址,但是内容都是"hello"。
s3就等于 hello1, s4=hello2
因此 s1==s3 是肯定的。指向同一个"hello"对象hello1.
但是 s1==s4 应该是错的。因为 一个指向hello1 一个指向hello2.
但是结果却是两个都是true,这个问题当时是百思不得其解;一开始我的想法是是不是intern()方法优先选择了先创建的s1。
one hundred years后找到了答案:
1.是在类加载过程中就已经被更新了。
2.是在真正执行代码时,遇到字面量或者intern()语句,就将其尝试加入字符串常量池。
3.两者都具备, 类加载过程是初始化常量池。 执行代码时是新增常量池的内容。并且对于常量池中已有的字面量,不采取更新措施。