【问题标题】:How does Java's intern() method behave on such Strings that are created during run timeJava 的 intern() 方法如何处理在运行时创建的此类字符串
【发布时间】:2020-12-21 10:36:24
【问题描述】:
class Demo
{  
    public static void main(String[] args)  
    {  
        String s1 = new String("ABC");
        String s2 = s1.concat("ABC");
        String s3 = s2.intern();
        System.out.println(s2 == s3); //true

        String s4 = "ABCABC";
        System.out.println(s3 == s4); //true
    }  
}

字符串 s2 = s1.concat("ABC");将在堆区域创建一个新的字符串对象“ABCABC”。 字符串 s3 = s2.intern();应该在 String 常量池上创建一个新的 String 对象。

由于这是两个不同的对象,它们的引用不应该相等。但似乎我错过了与实习生()相关的重要内容。

【问题讨论】:

    标签: java string constants pool


    【解决方案1】:

    来自javadoc

    返回字符串对象的规范表示。 一个字符串池,最初是空的,由 String 类私下维护。 当调用 intern 方法时,如果池中已经包含一个等于此 String 对象的字符串,由 equals(Object) 方法确定,则返回池中的字符串否则,此 String 对象将添加到池中并返回对该 String 对象的引用。 因此,对于任何两个字符串 s 和 t,当且仅当 s.equals(t) 为真时,s.intern() == t.intern() 才为真。 所有文字字符串和字符串值的常量表达式都是实习的。字符串文字在 Java™ 语言规范的第 3.10.5 节中定义。

    因此,您的代码的内部字符串池演变如下:

    public static void main(String[] args)  
    {
        String s1 = new String("ABC");  
        // "ABC" generate a new string and add it to intern pool.
        // new String("ABC") uses the already present ABC string to generate a new copy that is not present in the pool. 
        // s1 points to that string that is not present in the internal pool
       
    
        String s2 = s1.concat("ABC"); 
        // "ABC" is already present. s1.concat("ABC") creates a new string ABCABC that is added to the internal pool. s2 point to that string   
    
        String s3 = s2.intern();         
        // s2.intern() returns a pointer to ABCABC that is already present in the internal pool. s3 points to that string
    
    
       System.out.println(s2 == s3);
       // Prints true because ABCABC present in the internal pool is pointed by both s2 and s3.
    
        String s4 = "ABCABC";
        // ABCABC is already present in the internal pool. s4 points to that string
    
        System.out.println(s3 == s4);  
        // Prints true because all s2, s3, s4 points to the same instance ABCABC present in the internal pool.
    }  
    

    【讨论】:

      【解决方案2】:

      来自interndocs

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

      在调用intern之前,字符串ABCABC实际上已经在字符串池中了!并且根据上面,如果字符串已经在池中,intern 只是从池中返回字符串,而不是在池中创建一个重复的字符串。这与从代码中的文字创建的字符串相同。

      “为什么字符串已经在池中?”我听到你问。这是因为字符串在源代码中首次出现时并未添加到字符串池中。编译器收集源代码中的所有字符串文字,并将它们放入类文件中。当你的程序开始运行时,类文件中的所有字符串都被放入字符串池中。

      要实际看到您期望的行为,您不能使用字符串文字"ABCABC"。例如,您可以将其替换为"ABC".concat("ABC")

      String s4 = "ABC".concat("ABC"); // this creates a new string
      

      【讨论】:

      • concat() 在执行之前无法确定最终字符串的外观。字符串 s2 实际上会分配在堆区域中。考虑这个例子, class Main { public static void main(String[] args) { String s1 = new String("ABC");字符串 s2 = s1.concat("ABC");字符串 s3 = "ABCABC"; System.out.println(s2 == s3);如果 s2 已经在池中,那么为什么 s2 == s3 会导致错误。
      • @ZabiKhan 请不要在发布答案后更改您的问题,因为这会使答案无效。我已回滚您的编辑。
      • @ZabiKhan 所以你问为什么s2 == s3 是真的?然后你的问题会像我说的那样被关闭。
      • 更正:将编译时常量添加到池中的时间取决于实现。在这个特定的例子中,当s2 == s3true 时,证明"ABCABC"不在池中。否则,s1.concat("ABC") 的结果无法成为池成员。然后,当String s4 = "ABCABC"; 被执行时,常量被解析为与String s3 = s2.intern(); 添加的相同对象。所以s3 == s4 保证为true,但在急切不断解析的环境中s2 == s3 将是false
      • 通常“移至常量池”和“留在堆中”都是不恰当的短语。所有对象都存储在堆中。运行时字符串池只是一些字符串的references 表。向池中添加字符串意味着添加引用。字符串对象本身保持在原来的位置。
      【解决方案3】:

      在我看来,intern方法如果没有字符串,则将字符串添加到字符串常量池中,然后返回字符串引用。

      所以,s4="ABCABC" aready 已经存在字符串池

      【讨论】:

      • 请重新访问问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多