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

代码如下:

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); 

上面代码的输出是:

 false true 

我知道ss2是不同的对象,因此结果的计算结果为false,但第二个结果的计算结果为true。 谁能告诉我差异?

这是发生了什么:


例1

 String s1 = new String("1"); s1.intern(); String s2 = "1"; 
  1. 字符串文字"1" (传递给String构造函数)在地址A处实现
    字符串s1在地址B创建,因为它不是文字或常量表达式。
  2. intern()的调用无效。 字符串"1"已经实现,操作结果未分配回s1
  3. 从字符串池中检索值为"1"字符串s2 ,因此指向地址A.

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


例2

 String s3 = new String("1") + new String("1"); s3.intern(); String s4 = "11"; 
  1. 字符串s3在地址C创建。
  2. intern()的调用将地址C处的值为"11"的字符串添加到字符串池中。
  3. 从字符串池中检索值为"11"字符串s4 ,因此指向地址C.

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


概要

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

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

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

对于场景1:

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

使用字节码

  0: new #2 // class java/lang/String 3: dup 4: ldc #3 // String 1 6: invokespecial #4 // Method java/lang/String."":(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"); 它将创建一个新的String对象,它将有一个“1”的新地址,它已经在String Pool中

ldc #3 // String 1

对于s2 ,作为字节码:

15: ldc #3 // String 1

s2指向String Pool变量: "1" ,因此ss2具有不同的地址,结果为false

对于场景2:

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

使用字节码

  0: new #2 // class java/lang/StringBuilder 3: dup 4: invokespecial #3 // Method java/lang/StringBuilder."":()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 

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

new #2 // class java/lang/StringBuilder

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

s3.intern() ,此方法将当前s3添加到Memory String Pool8: aload_1

并且s4正试图从中加载

ldc #8 // String 11

所以s3s4地址应相等,结果为真。

对于使用groovy的人来说,附加信息是: 行为不同

在此处输入图像描述

s.intern()不会更改字符串s。 你应该写:

  s = s.intern();