简体   繁体   English

Java中字符串实习的奇怪行为

[英]Strange behavior with string interning in Java

There is code as following: 代码如下:

String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);

String s3 = new String("1")+new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);

Output of the code above is: 上面代码的输出是:

false
true

I know that s and s2 are different objects, so the result evaluates to false, but the second result evaluates to true. 我知道ss2是不同的对象,因此结果的计算结果为false,但第二个结果的计算结果为true。 Can anyone tell me the difference? 有人能告诉我区别吗?

Here's what's happening: 这是发生了什么:


Example 1 例1

String s1 = new String("1"); 
s1.intern();
String s2 = "1";
  1. The string literal "1" (passed into the String constructor) is interned at address A . 字符串文字"1" (传递给String构造函数)在地址A处实现
    String s1 is created at address B because it is not a literal or constant expression. 字符串s1在地址B创建,因为它不是文字或常量表达式。
  2. The call to intern() has no effect. intern()的调用无效。 String "1" is already interned, and the result of the operation is not assigned back to s1 . 字符串"1"已经中断,操作结果未分配回s1
  3. String s2 with value "1" is retrieved from the string pool, so points to address A . 从字符串池中检索值为"1"字符串s2 ,因此指向地址A.

Result: Strings s1 and s2 point to different addresses. 结果:字符串s1s2指向不同的地址。


Example 2 例2

String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
  1. String s3 is created at address C . 字符串s3在地址C创建。
  2. The call to intern() adds the string with value "11" at address C to the string pool. intern()的调用将地址C处的值为"11"的字符串添加到字符串池中。
  3. String s4 with value "11" is retrieved from the string pool, so points to address C . 从字符串池中检索值为"11"字符串s4 ,因此指向地址C.

Result: Strings s3 and s4 point to the same address. 结果:字符串s3s4指向相同的地址。


Summary 摘要

String "1" is interned before the call to intern() is made, by virtue of its presence in the s1 = new String("1") constructor call. 字符串"1"在调用intern()被实现,因为它存在于s1 = new String("1")构造函数调用中。

Changing that constructor call to s1 = new String(new char[]{'1'}) will make the comparison of s1 == s2 evaluate to true because both will now refer to the string that was explicitly interned by calling s1.intern() . 将构造函数调用更改为s1 = new String(new char[]{'1'})将使s1 == s2的比较值为true,因为两者现在都将引用通过调用s1.intern()显式实现的字符串s1.intern()

(I used the code from this answer to get information about the strings' memory locations.) (我使用了这个答案中的代码来获取有关字符串内存位置的信息。)

For the scenario 1: 对于场景1:

String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);

with bytecode : 使用字节码

   0: new           #2                  // class java/lang/String
   3: dup
   4: ldc           #3                  // String 1
   6: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
   9: astore_1
  10: aload_1
  11: invokevirtual #5                  // Method java/lang/String.intern:()Ljava/lang/String;
  14: pop
  15: ldc           #3                  // String 1

for String s = new String("1"); for String s = new String("1"); it will create a new String object, it will have a new address with "1" that it is already in String Pool : 它将创建一个新的String对象,它将有一个“1”的新地址,它已经在String Pool中

ldc #3 // String 1

and for s2 , as the bytecode: 对于s2 ,作为字节码:

15: ldc #3 // String 1

s2 is pointing to String Pool variable: "1" , so s and s2 have the different address and result is false . s2指向String Pool变量: "1" ,因此ss2具有不同的地址,结果为false

For the scenario 2: 对于场景2:

String s3 = new String("1")+new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);

with bytecode : 使用字节码

   0: new           #2                  // class java/lang/StringBuilder
   3: dup
   4: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
   7: astore_1
   8: aload_1
   9: ldc           #4                  // String 1
  11: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  14: ldc           #4                  // String 1
  16: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  19: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
  22: astore_2
  23: aload_2
  24: invokevirtual #7                  // Method java/lang/String.intern:()Ljava/lang/String;
  27: astore_3
  28: ldc           #8                  // String 11

As the bytecode , you can see new String("1")+new String("1"); 作为字节码 ,您可以看到new String("1")+new String("1"); is created by using StringBuilder 是使用StringBuilder创建的

new #2 // class java/lang/StringBuilder

it's totally a new Object without String Pool variable. 它完全是一个没有String Pool变量的新Object。

and after s3.intern() , this method will add current s3 to the Memory String Pool and 8: aload_1 . s3.intern() ,此方法将当前s3添加到Memory String Pool8: aload_1

and s4 is trying to load from 并且s4正试图从中加载

ldc #8 // String 11

so s3 and s4 address should equal and result is true. 所以s3s4地址应相等,结果为真。

Just for someone who use groovy, the addition info is: the behavior is different 对于使用groovy的人来说,附加信息是: 行为不同

在此输入图像描述

s.intern() doesn't change the string s. s.intern()不会更改字符串s。 You should have written: 你应该写的:

    s = s.intern();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM