字符串常量池

28

正如这些Stackoverflow问题所解释的那样:问题1问题2,我理解当“字符串字面量”时会被interned

String s = "abc"; 

当JVM需要创建一个新的字符串对象而不是使用String Pool中已有的字符串对象时:
String s = new String("abc");

然而,在阅读以下两个类似的语句后,我有一个疑问。
来自SCJP preparation book
当编译器遇到字符串文字时,它会检查池中是否已经存在相同的字符串。如果找到匹配项,则将对新文字的引用定向到现有字符串,并且不会创建新的字符串文字对象。
来自JavaRanch: 在这种情况下,由于关键字“new”的存在,我们实际上会得到略微不同的行为。在这种情况下,对字符串文字的引用仍然被放入常量表(字符串文字池)中,但是,当您使用关键字“new”时,JVM有义务在运行时创建一个新的字符串对象,而不是使用常量表中的对象。

如果我们在使用“new”创建对象时,在非池内存和池内存中都放置了一个引用,根据上述定义,JVM难道不应该在执行此操作时返回相同的引用吗?

String one = new String("test");
String two = "test";

System.out.println(one.equals(two)); // true
System.out.println(one == two);      // false

因为在声明字符串字面值String three = "test";时,它已经存在于池中,所以应该返回相同的引用并打印true吗?还是前面的语句意味着它们将被放入池内存,但在使用new运算符时会被跳过?

只是补充一下,如果这有帮助的话,System.out.println(one.intern() == two);将返回true。 - Jeshurun
5个回答

33

也许这可以帮助您理解:

String literal = "test";
String one = new String(literal);
String two = "test";

System.out.println(literal == two); //true
System.out.println(one == two); //false

在你发的例子中:

String one = new String("test");
String two = "test";
构造函数String(String)接收的引用和two引用的值相同,是由于字符串池(interning)的作用。然而,被这两个引用所指向的字符串本身被用来构造一个新对象,并分配给了one引用。
在此示例中,确切地创建了两个具有值"test"的String:一个维护在常量池中,每当您在表达式中使用字面值"test"时引用它;另一个是通过 "new" 运算符创建并分配给one引用的。
编辑
也许你对这个说法感到困惑:
引用字面值字符串时,编译器会检查池中是否已存在相同的字符串。
请注意,这句话可能更清楚地表述为:
当编译器遇到一个字符串字面值时,它会检查池中是否已经存在一个相同的字符串。
只有当字符串被显式地 interned 或者类使用了字面量时,它们才会被放入池中。因此,如果您有例如下面这种情况:
String te = "te";
String st = "st";

String test = new String(te) + new String(st);

虽然在代码中存在一个值为test的字符串,但由于字面量"test"从未出现过,该字符串不会存在于池中。


这句话并没有提到堆内存!:/ - Anand Varkey Philips
@AnandVarkeyPhilips:问题也没有。如果您有相关的问题,请随时发布。 - Mark Peters
嗨@Mark,你能告诉我使用new运算符创建"test"时会在什么时候被创建吗?如果我添加新问题,它可能会被标记为重复。这就是为什么... - Anand Varkey Philips

8
    //Creates a new object even if one exists in the pool
    String s1 = new String("Tendulkar");

    // makes a new object string and then the reference is available to the pool
    String s2 = s1.intern();

    //this object is not created but references the address present in the pool
    String s3 = "Tendulkar";

    System.out.print(s1==s2); // results in false
    System.out.print(s2==s3); //very very true !!!

2
您的问题:
当我们使用“new”关键字创建对象时,如果我们在非池内存和池内存中都放置了引用,并且基于上述定义。那么 JVM 在执行此操作时是否应该返回相同的引用?
答案:
当您使用 new 关键字创建新字符串对象时,生成的地址将是堆地址,而不是字符串常量池地址。两个地址是不同的。
问题:
String one = new String("test");
String two = "test";

System.out.println(one.equals(two)); // true
System.out.println(one == two);      // false

这些语句的意思是,当使用new运算符时,它们会被放入池内存中,但会被简单地跳过吗?

答案:是的,您的假设是正确的。当程序员使用new关键字时,JVM会忽略字符串常量池,并在堆中创建一个新副本。因此,两个地址不同。


2

"abc" 将一个对象放入常量池中,在编译/类加载时,而new String()则是在执行时创建一个新的对象。所以new String("abc") 两个阶段都有所涉及。


0

字符串字面量的创建

  1. 每次创建字符串字面量时,JVM 首先检查字符串常量池

  2. 如果字符串已经存在于池中,则返回对该实例的引用

  3. 如果字符串不存在于池中,则创建一个新的字符串实例并放入池中

例如,

    String s1 = "Welcome";
    String s2 = "Welcome"; //will not create new instance

字符串字面量和字符串对象

    String str1 = "Hello World!!";
    String str2 = "Hello World!!";
    System.out.println(str1 == str2);// true

当创建字符串字面量str2时,“Hello World”字符串不会再次创建。相反,它会重用str1中已经存在于字符串常量池中的字符串。

由于str1和str2都指向同一个字符串,因此它们是相同的。

    String str3 = new String("Hello World!!");
    String str4 = new String("Hello World!!");
    System.out.println(str3 == str4); // false

在这种情况下,新的String对象被创建并由str3和str4分别引用。因此,str3 == str4为false。字符串在池中,str1 == str2为true。

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