【问题标题】:Differences between "abc" + "de" and "abc" + de (de = "de") in Java?Java中“abc”+“de”和“abc”+de(de =“de”)之间的区别?
【发布时间】:2014-08-13 09:46:19
【问题描述】:

我运行以下代码并获得 cmets 中显示的结果。我知道==.equals() 之间的区别。我不明白的是为什么我在第二行的代码与第三行的代码有不同的结果。

    String de = "de";
//  String abcde = "abc" + "de"; // abcde == "abcde" reture true
    String abcde = "abc" + de;   // abcde == "abcde" reture false;
    System.out.println();
    System.out.println(abcde=="abcde");
    System.out.println(de=="de");

在尝试调试时,我使用了 javap -c 命令并为第一个字符串连接获得了以下输出“代码”:

         Code:
 0:   ldc     #9; //String de
 2:   astore_1
 3:   new     #10; //class java/lang/StringBuilder
 6:   dup
 7:   invokespecial   #11; //Method java/lang/StringBuilder."<init>":()V
 10:  ldc     #4; //String abc
 12:  invokevirtual   #12; //Method java/lang/StringBuilder.append:(Ljava/lang
 String;)Ljava/lang/StringBuilder;
 15:  aload_1
 16:  invokevirtual   #12; //Method java/lang/StringBuilder.append:(Ljava/lang
 String;)Ljava/lang/StringBuilder;
   19:  invokevirtual   #13; //Method java/lang/StringBuilder.toString:()Ljava/l
 ng/String;
   22:  astore_2
   23:  getstatic       #14; //Field java/lang/System.out:Ljava/io/PrintStream;
   26:  invokevirtual   #15; //Method java/io/PrintStream.println:()V
   29:  getstatic       #14; //Field java/lang/System.out:Ljava/io/PrintStream;
   32:  aload_2
   33:  ldc     #16; //String abcde
   35:  if_acmpne       42
   38:  iconst_1
   39:  goto    43
   42:  iconst_0
   43:  invokevirtual   #17; //Method java/io/PrintStream.println:(Z)V
   46:  getstatic       #14; //Field java/lang/System.out:Ljava/io/PrintStream;
   49:  aload_1
   50:  ldc     #9; //String de
   52:  if_acmpne       59
   55:  iconst_1
   56:  goto    60
   59:  iconst_0
   60:  invokevirtual   #17; //Method java/io/PrintStream.println:(Z)V
   63:  return

以及第二个字符串连接的输出:

我对这个“代码”不太熟悉,也看不出存在这些差异的任何原因。那么谁能解释为什么会出现这些差异?

相关post

【问题讨论】:

  • 这里的实际问题是什么? (我假设您已经知道 == 比较字符串 references,而不是字符串 contents)?
  • 请看String.equals(....)方法,它和==操作符不一样...
  • 要了解“那些代码”...您可以参考JVM instruction set document
  • JVM 在实例化字符串字面量时进行优化,即如果字符串字面量已经存在于 String Literal Pool 中,它每次都返回相同的引用。因此,所有具有值“abcde”的文字都指向同一个内存位置。 "==" 只比较引用,并且由于所有 "abcde" 的内存位置相同,因此它返回 true。 "abc"+de 使用 diff 内存地址创建一个新的 String 对象,因此 == 将它的内存地址与 "abcde" 进行比较,并且由于地址是 diff,它返回 false。
  • "==" 是浅比较。如果两个对象指向同一个内存引用,则返回 true。 “.equals”是深度比较。它按值比较两个对象,如果它们包含相同的值,则返回true。

标签: java string


【解决方案1】:

“问题”只是编译器对你来说太聪明了。当它看到"abc" + "de" 时,它立即将其连接到文字“abcde”中。但是当它看到 "abc" + de 时(根据 Java 规则)不允许将其“优化”为文字,而是必须实现 + 函数,创建一个新的 String 对象。

字符串字面量总是作为实习字符串处理,所以== 可以处理它们。

【讨论】:

  • 你的意思是“abc”+“de”在程序真正开始运行之前就成为字符串池的一部分吗?
  • @Tony - 编译器的设计使得像String a = "abc" + "de"; 这样的语句将被编译为String a = "abcde";。这是设计使然,允许多行字符串文字与+ 和其他类似的东西结合使用。
  • 感谢您的耐心等待。还有一个问题:常量池何时加载到 JVM 或内存中。谢谢。
  • 没有“常量池”。当一个类被加载时,类中的字符串字面量被转换为内部字符串对象,指向这些对象的指针存储在类的存储表示中。
  • 对不起,我不明白。有参考吗?
【解决方案2】:

它是 Java 编译器的产物。如您所知,== 比较对象引用,而不比较它们的内容。

Java 编译器实习生在源代码中看到的裸字符串。这就是为什么"x"=="x",因为只有一个"x" 字符串的副本,从源代码中实习,但是从值为"x" 的文件中读取字符串不会== 实习"x" ,因为它不是同一个对象。

Java 编译器对字符串上的+ 运算符也很聪明。它将在内部将var+"x" 转换为new StringBuffer().append(var).append("x").toString()。但它也会将字符串连接在一起并将它们作为另一个字符串进行实习,例如"abc"+"de" 将产生实习字符串"abcde",而不是new StringBuffer().append("abc").append("de").toString()

【讨论】:

    【解决方案3】:
        String de = "de"; // "de" is set during compile time and placed in the "String Pool".
    //  String abcde = "abc" + "de";// abcde == "abcde" reture true -- > String reference abcde will be set to "abcde" during compilation itself. and "abcde" will be placed in the String Pool.
        String abcde = "abc" + de;         // abcde == "abcde" reture false; abcde will not be set during compilation as the value of reference de will be resolved during runtime.
        System.out.println();
        System.out.println(abcde=="abcde");// false as --> String literal "abcde" on String pool != String Object "abcde" on heap.
        System.out.println(de=="de");// true--> de points to "de" on String pool
    

    【讨论】:

      【解决方案4】:

      问题与+ 无关,而在于您将字符串与== 进行比较。

      简答:使用"string".equals("string2")

      在 java 中,== 在与对象(如字符串)一起使用时是一个引用等于; “这两个名字指向内存中的同一个对象吗?”

      Java 有一个公共字符串的字符串池,它使用它而不是每次都创建一个新对象(这不是问题,因为字符串是不可变的),所以你的真/假问题取决于编译器是否足够聪明认识到这两个字符串是否相同,因此它可以使用相同的对象。不要依赖它。

      【讨论】:

        猜你喜欢
        • 2017-05-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-06-28
        • 1970-01-01
        • 1970-01-01
        • 2023-02-07
        • 1970-01-01
        相关资源
        最近更新 更多