Java中字符串连接运算符“+”中的混淆

时间:2015-11-19 10:24:07

标签: java string concat string-concatenation

据我所知,我们不能使用==运算符来比较Java中的String值。所以我写了下面的代码:

public class Test {
  public static void main(String[] args) {

    String s1 = "My Computer";
    String s2 = "My" + " Computer";

    System.out.println(s1 == s2);
  }
}

我期待结果为false,因为这是两个不同的对象,分配了不同的内存位置(如果我错了,请在这方面纠正我)。但是当我执行代码时,输​​出为true

然后我将s2的值更改为:

String str = "My";
String s2 = str + " Computer";      //instead of "My" + " Computer"

然后当我执行代码时,输​​出为false

现在我无法理解这两个语句的区别,尽管我在两个语句中都使用了+(而不是concat()方法)。任何人都可以解释一下。

3 个答案:

答案 0 :(得分:2)

Java为String对象使用了一个池 - 它试图变得聪明。这意味着当编译器可以确定您实际拥有相同的对象时,即使两个看似不同的对象上的==也会返回true。

然而,如果您想比较内容,应该避免通过==比较对象,因为这只会比较对象引用。对于内容比较,应使用equals

答案 1 :(得分:2)

您的测试用例存在愚蠢的错误。 String s2 = s1 + " Computer";s2字符串“我的电脑计算机”,而不是“我的电脑”。

如何进行字符串比较Java,请访问this链接。

Why String is immutable in Java - 一篇解释为什么无法修改Java中String类的实例的文章。请阅读此内容以获得清晰度。

答案 2 :(得分:2)

让你感到震惊的是this part of the specification

  

除非表达式是a,否则新创建String对象(第12.5节)   常数表达式(§15.28)。

因此,当您将字符串常量连接到另一个字符串常量时,它将计为一个常量表达式,因此将在编译时进行计算,并替换为字符串常量“我的电脑”。

您可以通过在已编译的类上运行javap -c来验证这一点。

public class Test {

    public static void main(String[] args) {

        String s1 = "My Computer";
        String s2 = "My" + " Computer";
        String s3 = "My";
        String s4 = s3 + " Computer";

        System.out.println(s1 == s2); //true
        System.out.println(s1 == s4); //false
    }
}

编译为:

  public static void main(java.lang.String[]);
    Code:
       // s1 = "My Computer"
       0: ldc           #2                  // String My Computer
       2: astore_1

       // s2 = "My" + " Computer"
       3: ldc           #2                  // String My Computer
       5: astore_2

       // s3 = "My"
       6: ldc           #3                  // String My
       8: astore_3

       // s4 = s3 + " Computer"
       9: new           #4                  // class java/lang/StringBuilder
      12: dup
      13: invokespecial #5                  // Method java/lang/StringBuilder."<
init>":()V
      16: aload_3
      17: invokevirtual #6                  // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      20: ldc           #7                  // String  Computer
      22: invokevirtual #6                  // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      25: invokevirtual #8                  // Method java/lang/StringBuilder.to
String:()Ljava/lang/String;
      28: astore        4

      ... the rest of the code omitted 

如您所见,前两个分配(s1s2)加载完全相同的常量(#2),因此使用相同的对象。而s4的赋值是而不是定义为constant expression(尽管一个足够聪明的编译器可以解决它,但是不允许),因此你得到了整个“创建一个StringBuilder,将字符串追加到它,将结果转换为新的字符串”进程。

有趣的是,如果在上面的代码中将final修饰符添加到s3,则会使s3 + " Computer"再次成为常量表达式,并且两个比较都将打印{{1} }。

毫无疑问,您已经知道,您的代码的正确性不能依赖于所有这些,但知道它是一件有趣的事情。

相关问题