本文参考 https://blog.csdn.net/believesoul/article/details/79588305 ,我对这篇文章进行了一些小小的整理和修改。这篇文章很好的对intern()方法进行了阐述。

最近在看字符串相关知识的时候,又看到了关于字符串创建方式和是否相等的相关问题,回忆起之前碰到的一个String中的intern()方法,它是一个本地方法(由Java语言外的语言编写),因此在jdk1.8源码中没有其实现,不过有一段描述,讲述了它的作用。

intern()方法的描述

    /** * Returns a canonical representation for the string object. * <p> * A pool of strings, initially empty, is maintained privately by the * class {@code String}. * <p> * When the intern method is invoked, if the pool already contains a * string equal to this {@code String} object as determined by * the {@link #equals(Object)} method, then the string from the pool is * returned. Otherwise, this {@code String} object is added to the * pool and a reference to this {@code String} object is returned. * <p> * It follows that for any two strings {@code s} and {@code t}, * {@code s.intern() == t.intern()} is {@code true} * if and only if {@code s.equals(t)} is {@code true}. * <p> * All literal strings and string-valued constant expressions are * interned. String literals are defined in section 3.10.5 of the * <cite>The Java&trade; Language Specification</cite>. * * @return a string that has the same contents as this string, but is * guaranteed to be from a pool of unique strings. */
      public native String intern();
返回字符串对象的规范表示形式。

字符串池最初是空的,由类字符串单独维护。

调用intern方法时,如果池中已经包含一个字符串,该字符串等于由equals(object)方法确定的该字符串对象,则返回池中的字符串。否则,该字符串对象将添加到池中,并返回对该字符串对象的引用。

因此,对于任意两个字符串s和t,只有当s.equals(t)为真时,s.intern()==t.intern()才为真。

所有的文字字符串和字符串值常量表达式都是内部的。String文字在Java语言语言规范的3.3.5节中定义。



返回:

一个字符串,其内容与此字符串相同,但保证来自唯一字符串池。

外部注释:

@org.jetbrains.annotations.not空

归纳一下的话,它的作用在jdk1.7之后是查看常量池中是否存在和调用方法的字符串内容一样的字符串,如果有的话,就返回该常量池中的字符串,若没有的话,就在常量池中写入一个堆中该字符串对象的一个引用,指向堆中的该对象,并返回该引用。
用代码说话吧,注释中有我对intern()方法的理解,过程中查阅了部分源码和相关博客,如果有谬误,请大家指出,互相交流学习,共同进步。

intern方法的测试(jdk1.8)

package com.bestqiang.set;

/** * @author BestQiang */
public class Test {
    public static void main(String[] args) {
        // 第一次,创建了两个对象,一个是堆中的string对象,一个是常量池中的"abc"
        String a = new String("abc");

        // 第二次,创建一个对象,堆中的另外一个string对象
        String b = new String("abc");

        /* * intern方***到常量池中查找是否存在该对象,如果存在,返回该对象。不存在的话就创建该对象并返回该对象(jdk1.6), (jdk1.7) * 会在常量池中存一个指向堆中的那个对象的引用。 不存在往往是String s3 = new String("1") + new * String("1");这种形式,会在堆中有一个s3指向的11的对象和常量池中的1对象 * 在这里就是体现的堆中的内存地址不一样,但对应的同一个常量池中的string 第一个比较时常量池中的该对象和自身比较 * 下面两个比较则是常量池中的对象和堆中的两个对象进行比较 */
        System.out.println(a.intern() == b.intern());// true
        System.out.println(a.intern() == b);// false
        System.out.println(a.intern() == a);// false

        // 直接从字符串常量池中获取
        String poolstr = "abc";

        /* * 这里新声明并赋值了一个poolstr,值为常量池中的字符串"abc",将它和a.intern()和b.inten()比较就是和自身比较 */
        System.out.println(a.intern() == poolstr);// true
        System.out.println(b.intern() == poolstr);// true

        /* * str创建了3个对象,在堆中有一个"ab",在常量池中有一个"a"和"b" 比较str.intern()和str会得到true * 在jdk1.7之后,会在常量池中存一个指向堆中的那个对象的引用。 * 调用str.intern()会在常量池中存储一个指向堆中"ab"的引用,也就是说它和堆中的对象实际是等价的,因此==时返回true */
        String str = new String("a") + new String("b");
        System.out.println(str.intern() == str);// true

        /* * 常量池中已存在ab,所以会直接将strtwo指向常量池中的"ab",即堆中str对象的引用,因此相等 */
        String strtwo = "ab";
        System.out.println(strtwo == str);// true

    }
}

jdk1.8环境下运行结果:

true
false
false
true
true
true
true

其他问题

  • 其中有提到一个new String(" ")会创建几个对象,一般就是一个或两个,主要是看常量池中是否已经有了该字符串,在上述代码在也有所提及。

对于每一个比较语句我都做了自己的理解的标注,比较通俗易懂,仔细看一下就能理解intern方法的作用,另外附上一篇博文,是一篇讲述intern方法的实现原理的博文,对我理解intern方法有很大帮助,感兴趣的朋友可以看一下。
Java技术——你真的了解String类的intern()方法吗

  • 其中提到了 jdk1.7将常量池从方法区中转移到堆 中对intern()方法的影响,非常清晰的描述了差别,配合本博文实例更适宜使用,本博文更多的从实现角度来解析intern()方法,希望对您有所帮助

如有谬误,敬请指正。
欢迎讨论。