字符串文字的行为令人困惑

时间:2013-05-24 06:41:44

标签: java string string-interning

String literals的行为在下面的代码中非常混乱。

我可以理解第1行,第2行和第3行是true,但为什么第4行false

当我打印两者的哈希码时,它们是相同的。

class Hello
{
   public static void main(String[] args)
   {
      String hello = "Hello", lo = "lo";
      System.out.print((Other1.hello == hello) + " ");     //line 1
      System.out.print((Other1.hello == "Hello") + " ");   //line 2
      System.out.print((hello == ("Hel"+"lo")) + " ");       //line 3
      System.out.print((hello == ("Hel"+lo)) + " ");         //line 4
      System.out.println(hello == ("Hel"+lo).intern());      //line 5
      System.out.println(("Hel"+lo).hashCode());   //hashcode is 69609650 (machine depedent)
      System.out.println("Hello".hashCode());       //hashcode is same WHY ??.
   }
}

class Other1 { static String hello = "Hello"; }

我知道==检查引用相等性并在池中检查文字。我知道equals()是正确的方法。我想理解这个概念。

我已经检查了这个question,但没有明确解释。

我希望得到完整的解释。

11 个答案:

答案 0 :(得分:26)

String类型的每个compile-time constant expression都将放入字符串池中。

基本上这意味着:如果编译器可以(轻松地)“计算”String的值而不运行程序,那么它将被放入池中(规则稍微复杂一点,并且具有一些极端情况,请参阅上面的链接了解所有细节。)

对于第1-3行中的所有字符串都是如此。

"Hel"+lo 不是编译时常量表达式,因为lo是非常量变量。

哈希码是相同的,因为the hashCode of a String depends only on its content。这是equals()hashCode()的合同所要求的。

答案 1 :(得分:2)

在运行时通过串联计算的字符串是新创建的,因此是不同的

这是一个阅读链接:http://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.5

答案 2 :(得分:2)

可以通过以下方式创建String对象:

String str = new String("abcd");  // Using the new operator 
                                  // str is assigned with "abcd" value at compile time.

String str="abcd";         // Using string literal
                           // str is assigned with "abcd" value at compile time.

String str="ab" + "cd";    // Using string constant expression.
                           // str is assigned with "abcd" value at compile time.
String str1 = "cd";
String str = "ab"+str1;    // Using string expression.
                           // str is assigned with "abcd" value at run time only.

和Hashcode将仅在运行时根据String对象的内容计算。

答案 3 :(得分:1)

这是因为这个实例中的comipler不够聪明,可以解决它可以在相同的字符串文字中刻录。

Hashcode需要始终为相同的字符串返回相同的值(调用它上面的.equals返回true),这样会返回相同的结果。

答案 4 :(得分:0)

正如你已经知道的......这只是因为引用...当字符串来自池时它将具有相同的参考...但是当你做manuplations时,会生成一个带有新refrence的新字符串... < / p>

您可以查看此pooling concept

的链接

答案 5 :(得分:0)

因为遵循代码

("Hel"+lo)) + " "

内部翻译为

new StringBuilder("Helo").append(new String(lo)).append(new String(" ")).toString()

因此,您可以看到完全使用不同的String实例创建了一个新的String实例。这就是为什么你指向堆

中不同的内存位置时会出错的原因

答案 6 :(得分:0)

hashCode与对象引用没有任何关系(== check是参考比较器)。它可能有2个对象,其中hashCode返回相同的值,equals运算符返回true,但==返回false。这是它们是两个不同的对象,但具有相同的值。

我认为第4行返回false的原因是它是在运行时计算的值,因此是一个不同的字符串实例,具有不同的引用。

答案 7 :(得分:0)

字符串文字保存在特殊内存中,如果它们完全相同,则它们指向相同的内存映射。如果不创建文字字符串,则会创建一个新对象,因此它不会指向该内存,因此引用将不相同。

intern()方法告诉虚拟机将它放入内存的共享字符串文字地图中,以便下次执行该文字时,它会在那里搜索并指向它。

答案 8 :(得分:0)

第3行和第4行之间的差异如下:

•由常量表达式计算的字符串在编译时计算,然后将其视为文字。

•在运行时通过串联计算的字符串是新创建的,因此是不同的。

以上参考资料来自java规范。如果您需要更多说明,请告诉我。

http://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.5

答案 9 :(得分:0)

最后我知道了答案!

阅读Java SE 8规范第15.21.3节参考等式运算符==和!=(http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.21.3

  

虽然==可用于比较String类型的引用,例如   等式测试确定两个操作数是否引用   相同的String对象。

     

如果操作数不同,则结果为 false   字符串对象,即使它们包含相同的字符序列(§3.10.5)。可以测试两个字符串s和t的内容   通过方法调用s.equals(t)进行相等。

以下代码:

class Test {
    public static void main(String[] args) {
        String hello = "Hello";
        String lo = "lo";
        System.out.println((hello == ("Hel"+lo))); // line 3
    }
}

第3行中的语句(“Hel”+ lo)返回在运行时处通过串联计算的新字符串。

  

*在运行时连接计算的字符串是新创建的,因此是不同的。   (http://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#d5e1634

所以这段代码的结果是:

class Test {
    public static void main(String[] args) {
        String hello = "Hello";
        String lo = "lo";
        System.out.println((hello == ("Hel"+lo))); // line 3
    }
}

会导致:

false

由于,

此声明中的“Hello”对象:

String hello = "Hello";
本声明中的

和(“Hel”+ lo)对象:

System.out.print((hello == ("Hel"+lo)) + " ");

不同,但是:

  

*它们都包含相同的序列字符,即“Hello”。

     

*它们都具有相同的hashCode。

     

* hello.equals((“Hel”+ lo))将返回true。

答案 10 :(得分:0)

System.identityHashCode()将由默认方法hashCode()返回,这通常通过将对象的内部地址转换为整数来实现。