JDK7中String.Intern()方法的疑问

新手上路,请多包涵
String s1 = new String("aa") + new String("bb");
s1.intern();
String s2 = "aabb";
System.out.println(s1 == s2);//true

我已经了解,JDK6中intern() 会在常量池中不存在的情况下直接拷贝值放入常量池,而JDK7是会把堆中的引用放入常量池。
这段代码中的字面量"aa" "bb" "aabb" 在编译器就已经放入常量池中了,很多文章都说成执行到String s2 = "aabb"才把 “aabb”放入常量池,我认为是不对的。
所以在s1.intern()这个操作应该是没有产生任何副作用的,s1仍然指向堆中的一个对象,s2仍然指向常量池中的一个对象。
但实际结果却为true,而且把String s2 = "aabb"移动到s1.intern()前面,结果就是我所预期的false。到底是为什么呢?

阅读 1.7k
2 个回答

先说点别的:

  1. 这里的字符串拼接没有被优化
  2. "xxx"这样的字面量声明自带intern
  3. 不同版本的java效果各种不一样
  4. 一般的用法是String x = x.intern(),因为可能会返回常量池里的引用
  5. intern属于用时间换空间,一般也很少用到…
  6. 编译期没有字符串常量池,字符串常量池是运行期的东西,也就是说直到某个字符串字面量声明 或者 intern 之前,这个字符串不会进入字符串常量池中

再说你这个例子:

  1. 如果s1.intern()发生在s2的声明之前,s1进常量池,s2的声明自带intern,从常量池里找到s1赋值给自己,于是和s1是同一个引用
  2. 反过来,s2先声明,进常量池,s1需要计算,得到一个新的字符串,和s2就不是同一个了,这时候如果做s1.intern() == s2还是true

知乎大神原答案

找到一个答案,涉及到更深入的类加载机制
原答案比较混乱,我整理一下就是:

1.查看字节码文件中可以看到,定义一个字面量的String s2 = "aabb"对象,其实是存储了2个内容,一个是UTF8 aabb,一个是String对象指向这个UTF8,这个是重点,使用常量池中的数据,实际使用的是这个String对象。

2.加载类的时候会读取字节码文件中Constant pool里的内容放入运行期常量池,但这个时候只创建了UTF8 aabb这个对象(C中的Symbol类型),配套的String对象是懒加载的,只有当第一使用到这个常量的时候才会在常量池中去创建这个String对象。

3.再结合问题中的代码,程序开始执行后,常量池中已经有"aa" "bb" "aabb"3个UTF8对象,但是并没有可用的String对象。当执行完第一行代码,会创建常量池中的"aa""bb"String对象;当程序执行到s1.intern();时,常量池中并不存在"aabb"这个String对象,于是将s1的引用放入常量池作为String对象,后面代码再去常量池获取这个字面量的时候,返回的就是s1的地址,所以s1==s2

4.推测,上面这种情况,类加载时候创建的"aabb"Symbol对象就无用了,未证实。

5.当先执行String s2 = "aabb";的时候,会直接去常量池中获取对象,发现常量池中的String对象不存在,于是创建一个,指向常量池中的UTF8 aabb对象。下一步再执行在s1.intern();的时候,发现常量池中已经存在,放不进去了,所以结果是s1指向堆中的一个对象,s2指向常量池中的一个对象,2者并不相等。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题