添加String Literals和String对象之间的区别

时间:2011-04-13 12:32:28

标签: java string literals

添加String Literal和String Object之间有什么区别?

例如

    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相同的位置?

4 个答案:

答案 0 :(得分:3)

因为您正在比较参考文献。要比较内容,请使用s1.equals(s2)

如果你的引用比较是故意的,那么你不清楚为什么你期望编译器/ JVM以不同的方式实习或不实习相同的字符串。

答案 1 :(得分:3)

由于s1 + s2不是常量表达式,因为s1s2不是final,因此其结果不会被实现,即创建另一个对象来表示它,因此引用比较产生false

JLS 3.10.5 String Literals

  

字符串文字 - 或者更常见的是,作为常量表达式(第15.28节)的值的字符串 - 是“interned”,以便使用String.intern方法共享唯一的实例。

JLS 15.28 Constant Expression

  

编译时常量表达式是表示基本类型值的表达式或不突然完成的字符串,仅使用以下内容组成:

     
      
  • ...
  •   
  • 引用常量变量的简单名称(§4.12.4)。
  •   

JLS 4.12.4定义final个变量。

如果您将s1s2声明为final,则s3 == s5将为true

答案 2 :(得分:1)

编辑:我假设您知道您正在比较引用,而不是字符串的内容。如果不是,s3.equals(s5)就是您要找的(如上所述)。

s3由编译器优化为"hellohello1",也可以重用s4我很惊讶编译器不够智能,不能为s5做同样的事情。您使用的是哪个JDK版本?。此优化仅允许用于常量表达式(请参阅15.28 of Java Language Specification)。换句话说,对非最终变量的任何赋值都否定了以后优化的可能性。

这是一个简单类的javap -c -l的输出,它将你的代码包装成一个main方法(不是任何人都要求它,但我很好奇)。那么让我们看看发生了什么:

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并且在将结果存储在插槽5({{{{ 1}})。

答案 3 :(得分:0)

因为编译器优化了字符串文字的连接。

在实践中,这应该无关紧要(大多数情况下),因为您通常希望使用equals方法比较Strings的相等性,而不是检查对象引用是否相同。

另请注意,您可以使用例如:

实习s5
s5 = s5.intern();

但这很少需要。