字符串常量池和实习生

String Constant Pool and intern

这几天一直在努力理解String常量池和inter的概念,看了很多文章,了解了一部分,但还是有点迷茫:-

1.String a = "abc" 这会在字符串常量池中创建一个对象 但是下面这行代码是否在字符串常量池中创建了对象"xyz"? String b = ("xyz").toLowerCase()

2.

String c = "qwe"   
String d = c.substring(1)    
d.intern()   
String e = "we" 

是否应该在 class 加载期间将文字 "we" 添加到 String consant 池中,如果是这样,为什么 d==e 即使 d 未指向 String 也会导致 true常量池

正在延迟加载字符串池。如果您在字符串文字之前自己调用 intern(),那么这就是将进入字符串池的字符串版本。如果您自己不调用 intern(),那么字符串文字将为我们填充字符串池。

令人惊讶的是,我们可以在常量池之前影响字符串池;如以下代码片段所示。


要理解为什么这两个代码片段有不同的行为,重要的是要弄清楚

  1. the constant pool is not the same as the string pool。也就是说,常量池是存储在磁盘上的 class 文件的一部分,字符串池是填充有字符串的 运行 时间缓存。

  2. 并且引用字符串文字不会直接引用常量池,而是根据 Java 语言规范 jls-3.10.5;当且仅当字符串池中还没有值时,字符文字才会从常量池填充字符串池。

也就是说一个String对象从源文件到运行时间的生命周期如下:

  1. 由编译器在编译时放入常量池并存储在生成的class文件中(每个class文件有一个常量池)
  2. 常量池由 JVM 在 class 加载时间加载
  3. 当调用 intern 时,从常量池创建的字符串会在 运行 时添加到字符串池(如果等效字符串不存在,如果已经存在字符串,则在将使用字符串池) JVM Spec 5.1 - The Run-Time Constant Pool.
  4. intern 可以通过手动调用 intern() 显式发生,也可以通过引用字符串文字隐式发生,例如 "abc" jls-3.10.5.

以下两个代码片段之间的行为差​​异是由于在通过字符串文字隐式调用 intern 之前显式调用 intern() 造成的。

为清楚起见,这里是 运行 在此答案的评论中讨论的两种行为:

    String c = "qwe";   // string literal qwe goes into runtime cache
    String d = c.substring(1); // runtime string "we" is created
    d.intern();         // intern "we"; it has not been seen 
                        // yet so this version goes into the cache
    String e = "we";    // now we see the string literal, but
                        // a value is already in the cache and so 
                        // the same instance as d is returned 
                        // (see ref below)

    System.out.println( e == d );  // returns true

下面是我们在使用字符串文字后进行实习时发生的情况:

    String c = "qwe";   // string literal qwe goes into runtime cache
    String d = c.substring(1); // runtime string "we" is created
    String e = "we";    // now we see the string literal, this time
                        // a value is NOT already in the cache and so 
                        // the string literal creates an object and
                        // places it into the cache
    d.intern();         // has no effect - a value already exists
                        // in the cache, and so it will return e

    System.out.println( e == d );  // returns false
    System.out.println( e == d.intern() );  // returns true
    System.out.println( e == d );  // still returns false

以下是 JLS 的关键部分,说明为字符串文字隐式调用了 intern。

Moreover, a string literal always refers to the same instance of class String. This is because string literals - or, more generally, strings that are the values of constant expressions (§15.28) - are "interned" so as to share unique instances, using the method String.intern.

并且 JVM 规范详细介绍了从 class 文件加载的常量池的 运行 时间表示以及它与 intern 的交互。

If the method String.intern has previously been called on an instance of class String containing a sequence of Unicode code points identical to that given by the CONSTANT_String_info structure, then the result of string literal derivation is a reference to that same instance of class String. .