字符串相等性从char数组构造字符串或连接字符

时间:2015-11-29 20:33:23

标签: java

我理解方法内部:

String myStr1 = "good";
String myStr2 = "good";
System.out.println(myStr1==myStr2);

打印真实。 出于同样的原因:

String myStr1 = "good";
String myStr2 = ""+'g'+'o'+'o'+'d';
System.out.println(myStr1==myStr2);

也是如此。

然后原因:

String myStr1 = "good";
char[] myCharArr = {'g', 'o', 'o', 'd' };
String myStr2 = ""+myCharArr[0]+myCharArr[1]+myCharArr[2]+myCharArr[3];
System.out.println(myStr1==myStr2);

打印错误? 我没有看到最后两个代码之间的区别。 任何的想法? 感谢。

4 个答案:

答案 0 :(得分:3)

myCharArr[0]无法在编译时进行评估,因为编译器(谁的聪明才是限制器)认为在运行之前可能会在字符串连接之前对此数组进行编辑(也许是由其他一些线程)这意味着它的内容可以改变,因此它不会假设例如myCharArr[0]应该是'g'(可能在将来这种行为将得到改善)。

因此,虽然像""+'g'+'o'+'o'+'d'这样的代码确定它处理的值,但它可以确定结果字符串将是"good"(因为我们使用了编译时常量)所以优化我们的代码并且每次运行代码时都会阻止重新计算此表达式,只需将""+'g'+'o'+'o'+'d'替换为"good"
但由于它无法评估myCharArr[0]的这个表达式,因此无法以相同的方式优化我们的代码,这意味着它需要将此字符串的创建留给在运行时执行的代码。

现在,如果您想知道==true返回"good"=="good",为false等代码返回"good"==new String("good"),您需要知道:

  • ==比较引用,换句话说,它让我们测试我们是否正在比较存储相同对象的引用(如果要检查对象是否相等则使用equal方法)
  • Java有字符串池,它存储文字以避免重新创建存储相同数据的许多String对象,并且编译器添加了负责从该池放置和检索文字的代码,因此当您执行"good"=="good"时,两个文字都是该池中的相同对象其中true确认
  • 但是编译器没有添加负责放入池或从中检索的代码使用new Sring(data)构造函数显式地在运行时创建的字符串,以防止池中的字符串很可能不会再次被重新处理"good"==new String("good")您正在比较两个不同的对象,"good"来自池,而new String(...)是一个独立于池中的一个(确认false的结果==)。

答案 1 :(得分:3)

编译器替换从Constant表达式构建的多个值相等的 String ,如下所示:

String myStr1 = "good";
String myStr2 = ""+'g'+'o'+'o'+'d';
System.out.println(myStr1==myStr2);

使用从 String.intern 获取的唯一String对象。然后将该唯一的String对象分配给这两个变量。这就是为什么他们的参考是平等的。

String myStr1 = "good";
char[] myCharArr = {'g', 'o', 'o', 'd' };
String myStr2 = ""+myCharArr[0]+myCharArr[1]+myCharArr[2]+myCharArr[3];
System.out.println(myStr1==myStr2);

编译器无法对此进行优化,因为它具有不是常量表达式的数组引用。这导致两个单独的String对象,它们不是引用相等的。否则会违反Java语言规范。

以下是Java语言规范中的常量表达式的定义:

  

常量表达式是表示基元值的表达式   类型或字符串不突然完成并使用   只有以下内容:

     
      
  • 原始类型的文字和String类型的文字(§3.10.1,   §3.10.2,§3.10.3,§3.10.4,§3.10.5)

  •   
  • 转换为基本类型并转换为String类型(第15.16节)

  •   
  • 一元运算符+, - ,〜和! (但不是++或 - )(§15.15.3,   §15.15.4,§15.15.5,§15.15.6)

  •   
  • 乘法运算符*,/和%(§15.17)

  •   
  • 加法运算符+和 - (§15.18)

  •   
  • 轮班操作员<<>>>>>(§15.19)

  •   
  • 关系型运算符<<=>>=(但不是instanceof)(§15.20)

  •   
  • 等值运算符==!=(第15.21节)

  •   
  • 按位和逻辑运算符&amp;,^和| (§15.22)

  •   
  • 条件和操作员&amp;&amp;和条件或运算符|| (§15.23,§15.24)

  •   
  • 三元条件运算符? :(§15.25)

  •   
  • 包含表达式为常量表达式的带括号的表达式(第15.8.5节)。

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

  •   
  • TypeName形式的限定名称(第6.5.6.2节)。引用常量变量的标识符(§4.12.4)。

  •   
     

String类型的常量表达式总是“实例化”,以便使用String.intern方法共享唯一的实例。

     

常量表达式始终被视为FP-strict(第15.4节),即使   它发生在非常量表达式不会出现的上下文中   被认为是FP严格的。

消息来源:http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#d5e30892

答案 2 :(得分:1)

以下陈述

String myStr2 = ""+myCharArr[0]+myCharArr[1]+myCharArr[2]+myCharArr[3];

将编译为以下内容:

  1. StringBuilder sb = new StringBuilder()
  2. sb.append(myCharArr[0]) ... sb.append(myCharArr[3])
  3. 然后调用sb.toString(),返回新的String
  4. 反编译字节码,你会看到类似这样的东西

      28: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
      31: ldc           #4                  // String
      33: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      36: aload_1
      37: iconst_0
      38: caload
      39: invokevirtual #6                  // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
      42: aload_1
      43: iconst_1
      44: caload
      45: invokevirtual #6                  // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
      48: aload_1
      49: iconst_2
      50: caload
      51: invokevirtual #6                  // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
      54: aload_1
      55: iconst_3
      56: caload
      57: invokevirtual #6                  // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
      60: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
    

    其他陈述

    String myStr1 = "good";
    String myStr2 = ""+'g'+'o'+'o'+'d';
    

    声明两个常量字符串,这里是byte-code

       0: ldc           #2                  // String good
       2: astore_1
       3: ldc           #2                  // String good
       5: astore_2
    

    编译器会立即将它们声明为常量。

答案 3 :(得分:1)

只有编译时常量字符串会自动实现。在Oracle documentation中描述了被认为是常量字符串(通常用于常量表达式)。根据该定义,您的char数组不是常量,因此使用它的表达式将创建一个新的String对象。