String类intern方法的一个困惑的问题

[code="java"]String str1 = new StringBuilder("ja").append("va").toString();
System.out.println(str1 == str1.intern());
String str2 = new StringBuilder("计算机").append("aa技术").toString();
System.out.println(str2 == str2.intern());[/code]

结果为:false,true

[code="java"]String str1 = new StringBuilder("ja").append("av").toString();
System.out.println(str1 == str1.intern());
String str2 = new StringBuilder("计算机").append("aa技术").toString();
System.out.println(str2 == str2.intern());[/code]
结果为:true,true

求指点。

fenglei12
fenglei12 我得到的最终结果:jdk1.6下,intern()会把第一次遇到的字符串实例复制到常量池中,所以得到的都是false;而在1.7中,intern()会在常量池中记录第一次出现的实例的引用,所以会返回true,而返回false情况是因为java不符合首次出现的原则。
大约 6 年之前 回复
fenglei12
fenglei12 在jdk7下边实践试下,就明白了
大约 6 年之前 回复

6个回答

你的测试不对

[code="java"]
String x = "java";
System.out.println(x == x.intern());
[/code]
这样才是true,因为x,x.intern()都是指向pool的内存

[code="java"]
String str1 = new StringBuilder("ja").append("va").toString();

System.out.println(str1 == str1.intern());

[/code]
这才是false,因为str1指向堆内存(new的原因),str1.intern()指向的是pool的内存

haha662
haha662 String a = new String(new Char[]{'j','a','a','v'}) 这个会在堆里面会开辟一块内存,a指向这块内存,同时会在常量池里面放进字符串jaav(如果是第一次使用jaav)。 a.intern()是指向常量池中存储字符串jaav的内存块 a.intern() = "jaav".intern() 所以我觉得lz贴出来的不是实际测试结果
大约 6 年之前 回复
hz1011
hz1011 new肯定会开辟内存,其实我自己跑lz的用例也是全false,我只是根据lz的运行结果进行推测,常量池中的字符串是在调用intern()方法的时候用刚才new出来的字符串放进去的,例如: String a = new String({'j','a','a','v'});//开辟一个字符串内存"jaav" System.out.prinln(a==a.intern());//检测到常量池中没有该字符串,把a放进去,然后a.intern()返回的即为a本身,a==a.intern()为true. 只有这样可以解释的通,当然,如果lz的结果也是猜测的,不是实际结果,就另当别论了。
大约 6 年之前 回复
haha662
haha662 按照你的说法,就是因为不同的jvm实现可能不一样,如果常量池中,存在equals的值,然后使用new 操作符就不会在堆中开辟内存,而直接指向常量池内存块? 恕我见识浅薄,能给个具体的代码演示下吗?
大约 6 年之前 回复
hz1011
hz1011 常量池里边的字符串不是一直就存在着的,是在调用intern()方法的时候,如果常量池中没有跟该字符串equals的值,才会添加进去的,因为不同的jvm实现可能不一样,所以可能是有的jvm直接把当前的字符串拿来缓存(lz的这种情况),有的是新建一个相同的字符串进行缓存(会导致lz所有case都为false),如果lz的结果真的存在,那只有这个可以解释了。
大约 6 年之前 回复
haha662
haha662 "java"出现过,那也是在常量池里面出现啊,和堆里面的内存,永远不能是相等的,所以LZ的 String str1 = new StringBuilder("ja").append("va").toString(); System.out.println(str1 == str1.intern()); 肯定是false
大约 6 年之前 回复
hz1011
hz1011 new关键字肯定会开辟一块内存,创建一个新对象,但是如果以前内存中有"java"这个字符串,String x = "java"是不会开辟内存的,直接返回该字符串,例如: String a = "jaav"; String b = "jaav"; System.out.println(a==b); 输出为true lz这个问题需要确认的是,intern()里边的String是不是就是新new的String本身,一般第一次创建的String会被缓存,所以,唯一的可能是,lz跑testcase之前,"java"出现过。
大约 6 年之前 回复

我运行所有都是false啊

fxhu09
fxhu09 我的也都是false
大约 6 年之前 回复

你的测试是错误的,intern是将String置入pool,第一次的时候,肯定pool是没有的,所有应该都是false

一般第一次调用intern()时会把该String放入缓存池中进行缓存,因为"java"是关键字,之前很可能缓存池中已经存在了,所以用==判断肯定为false,其他的如果是第一次调用,intern()方法返回的即为该String本身,所以为true。
(另外吐槽一下iteye,我新手怎么了,每次都需要回答你那破测试,把我辛苦打的回复都搞没了,怪不得论坛都没人,快倒闭了,我FK!)

intern() 是将现在的字符串放入到字符串常量池中。而“[color=red]==[/color]”比较的是对象的引用地址,s1指向的是堆内存中的地址,而另外一个是常量池中的对象,所以返回的应该都是[color=red]false[/color]。

楼主的测试结果是没问题的。在JDK7下面就是这个结果。

intern()方法在JDK7里面有了一些改变,如果在JDK6测试,肯定都是false.

由于new StringBuilder("计算机").append("aa技术").toString();创建的字符串
会存在于常量池中。而intern()方法就直接返回了该字符串的引用。

而由于"java"是关键字,已经在常量池中存在了,所以intern()返回的值是JRE
放入的"java"常量的引用,之后StringBuilder创建出来的在堆上,因此用==比较
返回的是false.(见周志明《深入理解Java虚拟机》第二版P57)

但是,我不理解的是:
String str1 = new StringBuilder("1234").toString();

System.out.println(str1 == str1.intern()); // false

String str2 = new String("5678");

System.out.println(str2 == str2.intern()); // false

难道只有调了StringBuilder的append方法才会把值写入常量池?

fenglei12
fenglei12 你的解释非常正确,谢谢。
大约 6 年之前 回复
daydayupgoodboy
daydayupgoodboy FK.不能编辑的吗? 第一行是 String str2 = new StringBuilder("计算机").append("aa技术").toString();
大约 6 年之前 回复
daydayupgoodboy
daydayupgoodboy 上面第一行应该是 String str2 = new StringBuilder("计算机").append("aa").toString();
大约 6 年之前 回复
daydayupgoodboy
daydayupgoodboy String str2 = new StringBuilder("计算机").append("").toString(); String s3 = "计算机aa技术"; System.out.println(str2 == str2.intern()); // false Java中String的存储还真的是很绕啊。
大约 6 年之前 回复
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问