字符串文字和字符串对象的添加之间的区别

|| 字符串文字和字符串对象的添加有什么区别? 例如
    String s1 =\"hello\";
    String s2 =\"hello1\";
    String s3 =\"hello\" + \"hello1\";
    String s4 =\"hellohello1\";
    String s5 = s1 + s2;

    System.out.println(s3 == s4); // returns true
    System.out.println(s3 == s5); // return false
    System.out.println(s4 == s5); // return false
为什么
s3
/
s4
s5
不在同一位置?     
已邀请:
由于
s1 + s2
不是常数表达式,由于
s1
s2
不是
final
,因此其结果不会被求和,即创建另一个对象来表示它,因此引用比较将生成
false
。 JLS 3.10.5字符串文字:   字符串文字(或更一般而言,为常量表达式的值(第15.28节)的字符串)为“ interned \”,以便使用String.intern方法共享唯一的实例。 JLS 15.28常数表达式:   编译时常量表达式是表示原始类型或String的值的表达式,该值不会突然完成,并且仅使用以下内容组成:         ...   引用常量变量的简单名称(第4.12.4节)。    JLS 4.12.4定义了“ 7”个变量。 如果将
s1
s2
声明为
final
,则
s3 == s5
将是
true
。     
因为您正在比较参考。要比较内容,请使用
s1.equals(s2)
。 如果您对引用的比较是有意的,则不清楚为什么您期望编译器/ JVM内收或不内收以不同方式产生的相同字符串。     
编辑:我假设您知道您正在比较引用,而不是字符串的内容。如果否,那么您正在寻找ѭ16((如上所述)。 编译器将
s3
优化为
\"hellohello1\"
,ѭ2也重用了
s3
。我很惊讶该编译器不够聪明,无法对ѭ3进行相同的处理。您正在使用哪个JDK版本?仅对常量表达式允许这种优化(请参阅Java语言规范的15.28)。换句话说,对非最终变量的任何赋值都拒绝了后续优化的可能性。 这是一个简单类的ѭ21的输出,该类将您的代码包装到一个主要方法中(不是有人要它,但我很好奇我自己)。因此,让我们看看发生了什么:
public static void main(java.lang.String[]);
  Code:
    0:  ldc #16; //String hello
    2:  astore_1
    3:  ldc #18; //String hello1
    5:  astore_2
    6:  ldc #20; //String hellohello1
    8:  astore_3
    9:  ldc #20; //String hellohello1
    11: astore  4
    13: new #22; //class java/lang/StringBuilder
    16: dup
    17: aload_1
    18: invokestatic    #24; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
    21: invokespecial   #30; //Method java/lang/StringBuilder.\"<init>\":(Ljava/lang/String;)V
    24: aload_2
    25: invokevirtual   #33; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    28: invokevirtual   #37; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
    31: astore  5
    33: getstatic   #41; //Field java/lang/System.out:Ljava/io/PrintStream;
    36: aload_3
    37: aload   4
    39: if_acmpne   46
    42: iconst_1
    43: goto    47
    46: iconst_0
    47: invokevirtual   #47; //Method java/io/PrintStream.println:(Z)V
    50: getstatic   #41; //Field java/lang/System.out:Ljava/io/PrintStream;
    53: aload_3
    54: aload   5
    56: if_acmpne   63
    59: iconst_1
    60: goto    64
    63: iconst_0
    64: invokevirtual   #47; //Method java/io/PrintStream.println:(Z)V
    67: getstatic   #41; //Field java/lang/System.out:Ljava/io/PrintStream;
    70: aload   4
    72: aload   5
    74: if_acmpne   81
    77: iconst_1
    78: goto    82
    81: iconst_0
    82: invokevirtual   #47; //Method java/io/PrintStream.println:(Z)V
    85: return

LocalVariableTable: 
  Start  Length  Slot  Name   Signature
   0     86      0     args   [Ljava/lang/String;
   3     83      1     s1     Ljava/lang/String;
   6     80      2     s2     Ljava/lang/String;
   9     77      3     s3     Ljava/lang/String;
  13     73      4     s4     Ljava/lang/String;
  33     53      5     s5     Ljava/lang/String;


}
我不是有经验的阅读字节码,但我会努力的:) 以#开头的数字(例如#16)是对常量池的引用。内容总是作为注释添加到此行
ldc #16
后跟
astore_1
表示“装入常数#16并将其存储在插槽1中”。如您所见,这在插槽1-4的开头执行了4次,转换为s1,s2,s3和s4(请参阅LocalVariableTable)。 对于s5,无需过多说明,显然有一个StringBuilder并加载了插槽1(in25ѭ)和插槽2(
aload_2
),然后将结果存储在插槽5(
astore 5
)中。     
因为编译器优化了字符串文字的串联。 实际上,这并不重要(大部分时间),因为您通常希望使用equals方法比较Strings是否相等,而不要检查对象引用是否相同。 另请注意,您可以使用以下内容来实习s5:
s5 = s5.intern();
但是,这很少需要。     

要回复问题请先登录注册