在Java中,字符串的内部化

4
以下代码段将字符串变量设为内部变量。
String str1="my";
String str2="string";
String concat1=str1+str2;
concat1.intern();

System.out.println(concat1=="mystring");

表达式concat1=="mystring"返回true,因为concat1已经被内部化。


如果给定的字符串mystring如下代码段所示更改为string

String str11="str";
String str12="ing";
String concat11=str11+str12;
concat11.intern();

System.out.println(concat11=="string");

比较表达式concat11=="string"的结果是false。变量concat11所代表的字符串似乎没有被字符池缓存。我在这里忽视了什么?

我已经在Java 7更新11上进行了测试。


编辑:

完整的代码:

package test;

public final class Test
{
    public static void main(String... args)
    {
        String str11="my";
        String str12="string";
        String concat11=str11+str12;
        concat11.intern();
        System.out.println(concat11=="mystring");

        String str1="str";
        String str2="ing";
        String concat1=str1+str2;
        concat1.intern();
        System.out.println(concat1=="string");
    }
}

请注意,上述代码确实对字符串进行了内部化处理,但是你却丢弃了这些内部化的版本。如果你希望立即使用它们,则必须保存 intern 函数调用的结果。 - Hot Licks
1
String.intern()方法返回的是字符串的内部版本。由于Java中的字符串是不可变的,因此它永远不会更改原始字符串(即concat1)。无论如何,使用equals进行字符串比较(除非您有非常有说服力的理由使用intern)。 - Mattias Buelens
1
你能澄清一下,当你看到concat11返回false时,你是否运行了包含这两个片段的程序? - Dawood ibn Kareem
3
第一个例子有效是因为字符串已经被内部化,编译器发现它可以将str1和str2合并为单个文本时就这样做了。是否会这样操作取决于上下文,不能在一般情况下依赖此特性。 - Hot Licks
你能否把整个程序贴出来,这样我就可以像你一样运行它,并找到问题所在吗? - Dawood ibn Kareem
显示剩余5条评论
3个回答

6
如果您在同一个程序中运行这两个代码片段,那么concat1.intern()将把concat1添加到字符串池中。但是concat11.intern()不会向池中添加任何内容,因为"string"已经在池中(来自str2)。所以你最后的比较是比较concat11str2 - 这些不是同一个对象。
从Javadoc中可以看到:http://docs.oracle.com/javase/6/docs/api/index.html?java/lang/String.html

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


我已经在一个代码片段中注释掉了代码,但它总是返回 false,如果取的字符串是 string 则返回 true(如果字符串不是 string)。抱歉,我不理解。 - Tiny
1
一个问题。让我们只运行第一个示例中的代码。如果我们删除 concat1.intern();,结果将是 false,但如果我们让它保留,结果将是 true。但在另一个代码示例中,连接将返回 "string",注释掉 concat11.intern(); 将无关紧要,并且结果始终为 false。为什么? - Pshemo
concat11.intern() 不会改变字符串池,因为 "string" 已经存在于池中。因此,将其注释掉不会有任何变化。 - Dawood ibn Kareem

3
当您使用intern()方法处理之前不在字符串池中的字符串时,该字符串将被放置在其中,每个后续的文字都将使用对其的引用。
但是,如果字符串池中已经有某些文字,并且您尝试在具有相同字符的新创建字符串对象上调用intern()方法,则它将不会被放置在池中,而只会返回对池中字符串的引用。 它也不会更改对调用intern方法的原始字符串的引用。
由于Java在执行main方法之前加载和执行一些代码,因此在执行main代码之前,字符串池中可能会放置一些文字。
例如,字符串池将包含文字,如"UTF-8""charset""charsetName",这些文字可能来自java.lang.Stringjava.nio.charset.Charset类。看起来"string"文字是其中之一(我不确定它是何时添加到字符串池中的)。
因此,当您调用
String str11="str";
String str12="ing";
String concat11=str11+str12;
concat11.intern();

您的实习生不会将concat11中的"string"放入字符串池中,因为已经有一个字符串在那里了,并且其他"string"字面值(比如您在=="string"部分使用的这些)将使用字符串池中的一个,而不是concat11中的字符串。

让我们来测试一下这个理论:

String s = "ABCdef";// this string will be placed in 
                    // string pool before rest of code

String str1 = "ABC";
String str2 = "def";
String concat1 = str1 + str2;
concat1.intern();//this will only return reference to literal from pool

System.out.println(concat1 == "ABCdef");//false


// here we don't have any "ABcd" literal in pool yet
String strA = "AB";
String strB = "cd";
String concatAB = strA + strB;
concatAB.intern();//so this will put String object from concatAB to pool
System.out.println(concatAB == "ABcd");//and "ABcd" literal will use same object

输出

false
true

字符串池将包含字面量,例如UTF-8charsetcharsetName,这些字面量可能来自于java.lang.Stringjava.nio.charset.Charset类。看起来string字面量就是其中之一。如果真是这样的话,那么这就是答案。谢谢。 - Tiny
@Tiny,实际上这些字面量可以出现在许多标准类中,所以我不确定它们是否来自这些类。但由于我们不知道Java如何为字符串字面量创建String对象,例如使用哪个构造函数以及接下来调用了哪些方法,因此我认为这些字面量可能来自String类中的代码。 - Pshemo

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接