首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Java的字符串常量相关的一个问题

大家过年好!春节假期休了一个长假,今天刚回来。在知乎上遇到了一个很好的问题,忍不住回答了一下。原文转载过来了。

以下代码的运行结果,如何解释?

String h = new String("hw");

String h2 = h.intern();

String h1 = "hw";

System.out.println(h == h1);//false

System.out.println(h2 == h1);//true

System.out.println(h2 == h);//false

String s3 = new String("1") + new String("1");

s3.intern();

String s4 = "11";

System.out.println(s3 == s4);//true

第一,先搞清楚字符串直接量和加法运算的区别。

我们看这样一段代码:

把它编译完了以后,再使用javap -c来查看它的字节码是这样的:

看到了没有?s1直接调用了String的构造方法。但是s2不是,它实际上使用了StringBuilder,然后通过append方法把"s"和"2"串接起来,这个简单的加法实际上变成了与以下代码等价了:

第二,String的intern是什么意思?

intern方法是一个native方法,它的具体实现在hotspot的源代码里。我把它简化一下,贴上来:

看到这个代码,我们就知道了。当StringTable里没有某一个字符串的时候,调用intern的时候,就会把这个字符串添加到StringTable里去。

所以,这个代码的结果就容易理解了:

这个结果是true,就是因为intern的时候,其实就是把t1放到StringTable,并且直接把t1做为返回值赋给了t2。

第三,但是问题还没结束。字符串常量到底是怎么回事?本来这个问题快要清楚了,一出现字符串常量,一下子又复杂了。

看这样两个例子:

这个例子,按我们之前说的,h3和h是同一个对象,h3和h4是同一个对象,h和h1不是同一个对象,都可以解释了。h2实际上呢是一个字符串常量,它和h3是同一个对象好像也是对的。但我们调整一下h2的赋值,把h2放到h3之前,结果却变了:

注意,这一次,h2的赋值在前,h3在后,然后,我们看到h3和h就不再是同一个对象了。这是为啥呢?

这是因为字符串常量,在class文件的常量池中,当执行到ldc指令去访问这个常量的时候,如果该常量是一个字符串类型,hotspot就会在后面默默地创建一个字符串,并且,调用intern方法!

看到那个显眼的StringTable::intern了吗?问题就出在这里。

Java在加载字符串常量的时候会调用一遍intern,那么StringTable里就会留下这个hotspot默认创建的字符串。

好了。回到原问题。

h = new String("hw");

这条语句,"hw"是一个常量字符串,实际上,已经做过一次intern了,StringTable里保留的是hotspot默认创建的字符串。所以h2和h1会是相等的,都是StringTable里的这个默认字符串。

而s3因为是计算得来的,不是字符串常量,所以手动调用s3.intern()时,StringTable里留下的就是s3。再对s4赋值时,由于StringTable里已经有值了,所以不必再创建一次String对象,直接使用StringTable里的那个值就好了,其实就是s3,因此s3与s4是相同的对象。把s4的赋值放到s3之前再试一下。就可以验证了。

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180227G01VHW00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券