String.intern()探究

官方API解释

intern方法在实际开发中很少用到,但是其隐藏的技术点还是很重要的。那么,intern方法到底是干什么的呢?官方API如下(中文内容由本博主翻译^_^):

intern
public String intern()
Returns a canonical representation for the string object.
A pool of strings, initially empty, is maintained privately by the class String.//一个被String类独自维护的字符串常量池(相关概念可参考常量池、运行时常量池、字符串值基本概念区分Java内存区域简述),初始时常量池为空。

When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.//当intern方法被调用,如果字符串常量池中包含一个字符串和这个字符串对象的内容相同,那么就直接返回字符串常量池中的这个字符串。否则,将这个字符串放入池子中,返回这个字符串的引用。

It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true.

All literal strings and string-valued constant expressions are interned. String literals are defined in section 3.10.5 of the The Java™ Language Specification.

Returns:
a string that has the same contents as this string, but is guaranteed to be from a pool of unique strings.

用代码来解释API

api解释的有点抽象,下面举几个栗子并结合JVM详细的说一说。

//基于JDK1.8(JDK>=1.7时,字符串常量池是在堆上的。)
public static void main(String[] args) {
        String s = new String("1");
        System.out.println(s.intern() == s);//false

        String s3 = new String("1") + new String("1");
        System.out.println(s3.intern() == s3);//true
    }

下面就一行一行的分析下:

  • String s = new String("1"); 这句代码造成的结果是,在字符串常量池中生成“1”对象,在堆中给s分配内存区域。(下图中的①②③)

  • System.out.println(s.intern() == s);来看s.intern(),根据官方API,intern方法会首先去字符串常量池中寻找有没有和s内容相同的字符串,找到了“1”,所以直接返回字符串常量池中“1”的对象,即s.intern的地址为字符串常量池中的“1”,而s指向的是堆中的String对象,所以输出false。

  • String s3 = new String("1") + new String("1");new String(“1”)会生成两个对象,一个是在常量池中的“1”,另一个是在堆上分配的string对象,和分析的第一行代码完全一样。但是请注意:使用引号包含文本的方式创建的String对象之间使用“+”连接产生的新对象才会被加入字符串池中。对于所有包含new方式新建对象(包括null)的“+”连接表达式,它所产生的新对象不会被加入字符串池中。所以此代码最终的效果是:在堆中为s3分配内存区域,s3的内容为“11”,字符串常量池中会被加入字符串“1”,因为之前已经加入了“1”,所以不会重复加入,而“11”则不会被加入字符串常量池。(下图中的④⑤)

  • System.out.println(s3.intern() == s3);s3.intern会首先去字符串常量池中去寻找和s3内容相同的字符串,结果没找到,对于JDK1.7及其更高版本来说,因为字符串常量池在堆上,因此直接将s3的引用放入字符串常量池中,对于<=JDK1.6的版本,则直接在字符串常量池中生成一个新字符串“11”,返回指向此字符串的引用。博主用的是JDK1.8,因此会在字符串常量池中添加s3的引用。因此此行代码会输出true。(效果为下图的⑥)
    这里写图片描述

JDK版本的影响

上文说道JDK1.6及1.6之前字符串常量池在方法区中,JDK1.7及1.7之后字符串常量池在堆中。
<=JDK1.6的示意图

>=JDK1.7的示意图

再来段代码解释下:

//注释基于JDK1.6。
public static void main(String[] args) {
    String s = new String("1");//堆中为s分配内存空间,在常量池中放入字符串"1"。
    s.intern();//发现常量池中有字符串"1",因此直接返回常量池中"1"的引用。
    String s2 = "1";//双引号显示声明字符串,会首先去常量池中查找,结果找到了"1",所以直接返回常量池中"1"的引用。
    System.out.println(s == s2);//s指向堆中的对象,s2指向字符串常量池中的对象,显然不相等,因此输出false。

    String s3 = new String("1") + new String("1");//在堆中为s3分配内存空间,将字符串"1"放入字符串常量池。
    s3.intern();//在字符串常量池中寻找"11",结果没找到,直接在字符串常量池中new出一个内容为"11"的String对象,并返回此对象的引用。
    String s4 = "11";//双引号显示声明字符串,会首先去常量池中查找,结果找到了字符串"11",因此s4指向此字符串。
    System.out.println(s3 == s4);//一个在堆中,一个在字符串常量池中,因此返回false。
}

输出结果如下:
jdk6 下false false
jdk7 下false true

JDK1.7的结果在第一个例子的时候已经解释,所以这里只解释JDK1.6的结果(直接看注释)。

参考资料:

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值