字符串常量池和实习生

时间:2015-10-29 14:18:58

标签: java string string-literals

我最近几天试图理解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" 

字面意思"我们"在类加载期间添加到String consant池中,如果是这样,即使d未指向String Constant pool,为什么d==e会返回true

1 个答案:

答案 0 :(得分:8)

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

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

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

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

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

  3. 也就是说,从源文件到运行时的String对象的生命周期如下:

    1. 在编译时由编译器放入常量池并存储在生成的类文件中(每个类文件有一个常量池)
    2. 常量池由JVM在类加载时加载
    3. 从常量池创建的字符串在运行时被添加到字符串池中,因为调用了intern(如果还没有等效的字符串,如果已经存在字符串,那么将使用字符串池中的字符串) JVM Spec 5.1 - The Run-Time Constant Pool
    4. 实习生可以通过手动调用intern()或通过引用字符串文字(例如“abc”jls-3.10.5来隐式)来实现。
    5. 以下两个代码片段之间的行为差​​异是由于在通过字符串文字发生对实习生的隐式调用之前显式调用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的关键部分,声明实际上是为字符串文字调用实习生。

        

      此外,字符串文字始终引用类String的相同实例。这是因为字符串文字 - 或者更常见的是作为常量表达式(第15.28节)的值的字符串 - 被“实例化”以便使用String.intern方法共享唯一实例。

      JVM规范涵盖了从类文件加载的常量池的运行时表示的详细信息,它与实习生进行交互。

        

      如果先前在类String的实例上调用了String.intern方法,该类包含与CONSTANT_String_info结构给出的Unicode代码点序列相同的Unicode代码点序列,则字符串文字派生的结果是对同一个实例的引用class String。