String类的intern()方法,随常量池发生的变化

JVM的知识这里总结的很详细:https://github.com/doocs/jvm/blob/master/README.md,因此在本博客也不会再对其中的东西重复总结了。

intern的作用

简单的讲,intern方法是把调用者丢到常量池里,并返回一个引用指向它。

String s = new String("sq") + new String("666");      //line1
String s1 = s.intern();           //line2

以上代码line1在堆里创建两个字符串,并且拼接到一起。line2 是将这个拼接好的字符串放到常量池中,并返回一个指向常量池中的这个字符串的引用,在例子中赋给了s1。

为什么需要intern“多此一举”?因为是处理运行时生成的字符串

在上一篇讲常量池和JVM内存结构中说:初次创建字符串会自动在常量池中创建一个字符串。也就是说:new一个字符串时,会生产两个字符串,堆中一个,字符串常量池中创建一个,一共两个。那么,为什么还要调用intern() 方法呢?我的理解是:new字符串时自动在字符串常量池也创建一个字符串仅限编译时的情况,而intern则是将程序运行时生成的字符串的引用丢一份进字符串常量池。

像以上的情况,两个new运算符只是自动在池中创建了 “sq”   "666" 两个字符串,而没有 “sq666” ----因为这是在程序跑起来才通过“+”生成的。当程序跑到 line2 这里,就通过intern把  “sq666” 的引用也丢进池子。看如下例子:

片段1:
String s = new String("sq") + new String("6");      
s.intern();           
String s2 = "sq6";
System.out.println(s == s2)      //结果为true


片段2:
String s = new String("sq") + new String("6");              
String s2 = "sq6";
System.out.println(s == s2)      //结果为false

片段2 仅仅只比片段1少了 intern方法的调用,结果却相反。因为通过字面量创建字符串(即 String = "sq6")的方式只会生成一个字符串并放在常量池池中,且如果池中已有这个字面量的字符串,则直接把引用指向这个已有的字符串即可,而不需重复创建。

综上,在程序跑起来时生成了 "sq6" 字符串,片段2没有把它丢进池子,所以通过字面量创建时又在池子中创建了一个新的,二者的地址自然也不相等了。片段1却是先把运行时生成的 "sq6" 的引用丢进了池子,这样通过字面量创建时发现池子中有现成的,不需要创建新的。因此二者地址自然也相等啦。

intern 随常量池的变化---只丢引用了

上一篇说到:随着元空间取代永久代,字符串常量池也不再存在于永久代,也不存在于元空间,而是存在于堆中。intern方法也有一些改变,以前intern方法是把这个字符串丢一份拷贝进字符串常量池,在这之后intern方法只是把字符串的引用丢一份进了字符串常量池。(也有不同意见:说还是丢的字符串内容,而非引用)

我认为丢的是一份引用,因为对于上述代码片段1返回的是true。我们假设: 运行时生成的字符串"sq666",地址为 ’oxa‘ ,如果调用intern是丢了一份拷贝版的字符串到池中,那么被丢进池的拷贝版字符串地址就肯定不是‘oxa’了(假设为’oxb‘),最后执行:String s2 = "sq666"; s2的内容就是`oxb‘,片段1应该返回的就是false了。

一些注意点:

 String s = "sq"+"666";      经过优化是只在池中生成一个字符串的,即"sq666"

运行时生成字符串的情况还有: 调用StringBuilder的append方法等等

原文地址:https://www.cnblogs.com/shen-qian/p/11288839.html