繁体   English   中英

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

[英]Confusion in String concatenation operator “+” in Java

据我所知,我们不能使用==运算符来比较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()方法),但我无法理解这两个语句的区别。 任何人都可以解释。

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

但是,如果要比较内容,则应避免通过==比较对象,因为这只会比较对象引用。 对于内容比较,应使用equals

您的测试用例中有一个愚蠢的错误。 String s2 = s1 + " Computer"; 将为s2分配字符串“ My Computer Computer ”,而不是“ My Computer”。

有关如何进行Java字符串比较的信息,请访问链接。

为什么在Java中String是不可变的 -一篇文章解释了为什么不能在Java中修改String类的实例。 请阅读以清楚起见。

令您大跌眼镜的是此部分规范

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

因此,当您将一个字符串常量连接到另一个字符串常量时,该常量将视为一个常量表达式,因此将在编译时求值并替换为字符串常量“我的电脑”。

您可以通过在已编译的类上运行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 没有被定义为一个常量表达式 (即使有足够聪明的编译器可以计算出来,它是不允许),因此你会得到整个“创建一个StringBuilder,字符串追加到它,转换结果转换为新字符串”过程。

除了有趣的是,如果在上面的代码中将final修饰符添加到s3 ,这将使s3 + " Computer"再次成为常量表达式,并且两个比较都将输出true

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM