String用于提升性能的intern()方法

JDK每次升级都会做很多优化,我们使用最多的String常量类也在不断被优化。这次和大家分享的是JDK1.8中对String的优化之一,intern()方法的使用。


对应的方法及注释如下:

/**
* Returns a canonical representation for the string object.
*


* A pool of strings, initially empty, is maintained privately by the
* class {@code String}.
*


* 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.
*


* 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}.
*


* 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.
*
* @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();

一句话概括,就是通过常量池复用来节省内存空间、减少开销以提升性能。

Tips:

这里简单介绍一下常量池,方便下文理解。先看看JVM的内存结构:
在这里插入图片描述
我们的常量池就在方法区中,在实际应用中会跟堆区配合使用。平时大家都统称它为常量池,严格划分的话常量池又分为:静态常量池、运行时常量池和字符串常量池。三者的区别,有时间会写一篇单独的博客。


回到今天的主题,intern()方法。如果我们对着英文注释一句一句翻译,来理解它的话会很有限很苦涩,结合代码示例理解会更好一些(个人经验)。

示例一:

public static void main(String[] args) {

        String str1 = "abc";
        String str2 = new String("abc");
        String str3 = str2.intern();
        System.out.println("str1==str2: " + (str1 == str2)); // false
        System.out.println("str2==str3: " + (str2 == str3)); // false
        System.out.println("str1==str3: " + (str1 == str3)); // true
}

运行结果:

str1==str2: false
str2==str3: false
str1==str3: true

分析,str2.intern()被调用时,str1 = “abc” 已经将“abc”放入了常量池,根据方法注释,str3返回的是拿到的是常量池中的str1,str2还是对象引用。所以有了上面的结果。

示例二:

public static void main(String[] args) {

        String str1 = new String("abc");
        String str2 = new String("abc");
        System.out.println(str1 == str2); //false
        System.out.println(str1.intern() == str2.intern()); //true
}

运行结果:

false
true

分析,str1==str2为false就不用说了;str1.intern() == str2.intern()为true,在英文注释里有说:

s.intern() == t.intern() is true if and only if s.equals(t) is true.

也是就是两个String对象,当且仅当它俩equals()比较值为true,那它俩intern()的“==”操作也为true。


示例三:

public static void main(String[] args) {

        String str1 = new String("abc");
        String str2 = str1.intern();
        String str3 = "abc";
        System.out.println(str1 == str3); //false
        System.out.println(str3 == str2); //true
}

运行结果:

false
true

分析,new Sting() 会在堆内创建一个str1的String对象,“abc”会添加到常量池,在调用intern()方法时,会去常量池中查找是否有相等的字符串字面量和对象引用,此时有“abc”就返回了“abc”,所以str3 == str2为true。str1 == str3为false就不用说了。

示例四:

类成员变量也会add到常量池。

public static void main(String[] args) {

        Teacher teacher = new Teacher();
        teacher.setName("King");
        String name = teacher.getName();
        String str = "King";
        System.out.println(name == str); // true
    }

    static class Teacher {
        private String name;
        private Integer age;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Integer getAge() {
            return age;
        }

        public void setAge(Integer age) {
            this.age = age;
        }
    }

运行结果:

true

分析,类的成员变量在Class常量池中,英文文档上没有说明,但代码示例验证后,说明类成员变量也会放到常量池。

示例五:

public static void main(String[] args) {

        String s1 = new String("abc") + new String("abc");
        String s2 = s1.intern();
        String s3 = "abc" + "abc";

        System.out.println(s1 == s2); //true
        System.out.println(s2 == s3); //true
        System.out.println(s1 == s3); //true
}

运行结果:

true
true
true

分析,“new String(“abc”) + new String(“abc”)”首先会创建这两个对象以及相加后的对象,然后在常量池放入“abc”且对象指向该字面量。当s1调用intern()时因为没有“abcabc”的字面量,然后在常量池添加了s1的对象引用,返回给s2。所以,s1 == s2为true。““abc” + “abc””双引号相加,会判断这两个常量、相加后的常量在常量池上是否存在,

如果不存在
   则在常量池上创建相应的常量
  如果存在
   判断这个常量是存在的引用还是常量,
    如果是引用,返回引用地址指向的堆空间对象,
    如果是常量,则直接返回常量池常量

所以,““abc” + “abc””等同于“new String(“abc”) + new String(“abc”)”,s2 == s3与s1 == s3都为true。


花了不少时间分析、总结出来的,希望能帮助阅读者更快地理解它。
至此,本篇结束。

猜你喜欢

转载自blog.csdn.net/WLQ0621/article/details/107465898