概述

String::intern()是一个Native方法,用于返回该对象在常量池中的引用。

1
public native String intern();

作用:如果字符串常量池中已经包含一个等于该String对象的字符串,则返回代表池中这个字符串的String对象的引用;否则,会将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。

案例

  • 示例1:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/**
* - 在JDK 6中运行,会得到三个false;
* 在JDK 6中,intern()方法会把首次遇到的字符串实例复制到永久代(方法区)的字符串常量池中存储,
* 返回的也是永久代里面这个字符串实例的引用,而由StringBuilder创建的字符串对象实例在Java堆上,
* 所以必然不可能是同一个引用,结果将返回false。
*
* - 而在JDK 7后中运行,会得到一个true、一个false和一个true;
* 因为JDK7中,intern()方法实现就不需要再拷贝字符串的实例到永久代了,既然字符串常量池已经移到Java堆中,
* 那只需要在常量池里记录一下首次出现的实例引用即可。
* 因此intern()返回的引用和由StringBuilder创建的那个字符串实例就是同一个。
* 而对str2比较返回false,这是因为java这个字符串在执行StringBuilder()之前就已经出现过了,(在加载sun.misc.Version这个类的时候进入常量池的)
* 字符串常量池中已经有它的引用,不符合intern()方法要求“首次遇到”的原则,“JVM调优”这个字符串则是首次出现的,因此结果返回true。
* 而str3和str1一样,"JDKJVM"这个字符串则是首次出现。
*
*/
private static void compare() {
String str1 = new StringBuilder().append("JVM").append("调优").toString();
System.out.println(str1.intern() == str1);
// java这个字符串在执行StringBuilder()之前就已经出现过了,在加载sun.misc.Version这个类的时候进入常量池的。
/*
* 参考:https://www.zhihu.com/question/51102308/answer/124441115
* sun.misc.Version类会在JDK类库的初始化过程中被加载并初始化,
* 而在初始化时它需要对静态常量字段根据指定的常量值(ConstantValue)做默认初始化,
* 此时被 sun.misc.Version.launcher 静态常量字段所引用的"java"字符串字面量就被intern到HotSpot VM的字符串常量池StringTable里了。
*/
String str2 = new StringBuilder().append("ja").append("va").toString();
System.out.println(str2.intern() == str2);
// 而JDKJVM这个字符串则是首次出现
String str3 = new StringBuilder().append("JDK").append("JVM").toString();
System.out.println(str3.intern() == str3);
}

/**
* java中的String是引用类型。创建的String对象,实际上存储的是一个地址。
* 所以下面a和b都是引用类型,其存储的是字符串的地址。它们本身存储在Java虚拟机栈的局部变量表中。
* - a:直接将字符串存储在常量池中,然后将a指向常量池种中的"JVM"。
* - b:先将字符串"JVM"存储在常量池中,然后在heap中创建一个对象,该对象指向常量池中的"JVM",最后将b指向heap中创建的这个对象。
*
* 也就是说,a和b存储的内容是一样的,都是"JVM",但地址不一样:a中保存的是常量池中"JVM"的地址,b保存的是heap中那个对象的地址,
*
* 双等于号"=="比较的是地址,equals()比较的是内容。
*/
private static void compareStr() {
String a = "JVM";
// new一个对象
String b = new String("JVM");
// == 比较地址是否相等
// 都在运行时常量池中
System.out.println("JVM" == a); // true
System.out.println(a.intern() == a); // true
// a为字符字面量(存储在运行时常量池中),b为对象(存储在堆中),所以不等。
System.out.println(a == b); // false
System.out.println(a.intern() == b); // false

System.out.println(a.equals(b)); // true
}
  • 示例2:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public static void main(String[] args) {
    /*
    双等号"=="比较的是地址;equals()比较的是内容。
    */
    String s1 = "abc";
    String s2 = "abc";
    System.out.println(s1 == s2); // true
    System.out.println(s1.equals(s2)); // true

    String s3 = new String("abc");
    System.out.println(s1 == s3); // false
    System.out.println(s1.equals(s3)); // true
    System.out.println(s1 == s3.intern()); // true

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

评论