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

bin*_* he 15 java string

代码如下:

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);
Run Code Online (Sandbox Code Playgroud)

上面代码的输出是:

false
true
Run Code Online (Sandbox Code Playgroud)

我知道s并且s2是不同的对象,因此结果的计算结果为false,但第二个结果的计算结果为true.有人能告诉我区别吗?

Rob*_*sen 23

这是发生了什么:


例1

String s1 = new String("1"); 
s1.intern();
String s2 = "1";
Run Code Online (Sandbox Code Playgroud)
  1. 字符串文字"1"(传递给String构造函数)在地址A处实现.
    字符串s1在地址B处创建,因为它不是文字或常量表达式.
  2. 呼叫intern()无效.字符串"1"已经实现,并且操作的结果未分配回s1.
  3. 字符串s2与值"1"从字符串池检索,所以点,以解决一个.

结果:字符串s1s2指向不同的地址.


例2

String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
Run Code Online (Sandbox Code Playgroud)
  1. 字符串s3在地址C处创建.
  2. 调用intern()将带有"11"地址C值的字符串添加到字符串池.
  3. 字符串s4与值"11"从字符串池检索,所以指向地址Ç.

结果:字符串s3s4指向相同的地址.


摘要

字符串"1"在调用之前intern()被实现,因为它存在于s1 = new String("1")构造函数调用中.

更改构造函数调用s1 = new String(new char[]{'1'})将使s1 == s2evaluate 的比较为true,因为两者现在将引用通过调用显式实现的字符串s1.intern().

(我使用了这个答案中的代码来获取有关字符串内存位置的信息.)

  • 我的意思是"使用",就像你做的那样.我的观点是,我希望``a``在该语句执行之前很久就被实现,可能是在类加载时.我可能完全错了,但是以某种方式查看源代码会很好. (2认同)

che*_*ohi 12

对于场景1:

String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);
Run Code Online (Sandbox Code Playgroud)

使用字节码:

   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
Run Code Online (Sandbox Code Playgroud)

因为String s = new String("1");它会创建一个新String对象,它将有一个新的地址"1",它已经在String Pool中:

ldc #3 // String 1

并且s2,作为字节码:

15: ldc #3 // String 1

s2指向String Pool变量:"1",所以s并且s2具有不同的地址和结果false.

对于场景2:

String s3 = new String("1")+new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);
Run Code Online (Sandbox Code Playgroud)

使用字节码:

   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
Run Code Online (Sandbox Code Playgroud)

作为字节码,您可以看到new String("1")+new String("1");通过使用创建StringBuilder

new #2 // class java/lang/StringBuilder

它完全是一个没有String Pool变量的新Object .

之后s3.intern(),该方法将增加电流s3存储器串池8: aload_1.

s4试图从中加载

ldc #8 // String 11

所以s3s4地址应该相等,结果是真的.