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,但没有明确解释。
我希望得到完整的解释。
答案 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()
返回,这通常通过将对象的内部地址转换为整数来实现。