[英]Question about == operator in java
public class Demo {
public static void main(String[] args) {
String s1 = "Hello";
String s2 = "Hello";
System.out.println("s1 == s2 " + (s1 == s2));
String s5 = "Hel" + "lo";
String s6 = "He" + "llo";
System.out.println("s5 == s6 " + (s5 == s6));
String s7 = "He";
String s8 = "Hello";
s7 = s7.concat("llo");
System.out.println("s7 == s8 " + (s7 == s8));
String s10 = "He";
s10 = s10 + "llo";
System.out.println("s1 == s10 "+(s1 == s10));
}
}
在前面的代码中,s7 == s8和s1 == s10给出false。 有人可以解释一下,在s7 = s7.concat(“llo”)中实际发生了什么; 并且s10 = s10 +“llo”; 我理解==运算符检查引用和equal()检查对象的内容。 但我需要知道为什么s7和s10参考变量位模式与s8和s1不同。 如果这些事情与编译时生成的字符串和运行时生成的字符串有关,那么我如何识别它是编译时还是运行时字符串?
之所以发生这种情况,是因为Java在编译器中进行了优化。 当它看到你将文字字符串"Hello"
分配给s1时,它对s2使用相同的“Hello”,因为所有Java String操作都是非破坏性的(例如它们返回克隆而不是修改原始字符串),所以这是一件安全的事情。
同样的事情是"Hel" + "lo"
vs "He" + "llo"
; 它足够聪明地弄清楚它们是同一个东西。
其他的很复杂,无法对它们进行优化,因此您最终会得到单独的对象。
==不检查位模式,它将比较对象的内存地址。 只有同一个对象具有相同的内存地址。
克林特的答案很好,但我会进一步扩展它并在编译器级解释。
如您所知, s1
和s2
将最终成为对同一字符串实例"Hello"
引用。
对于s5
和s6
,编译器会看到常量表达式。 也就是说,它看到两个常量(字符串文字)之间的操作。 编译器知道如何添加字符串以及结果是什么。 由于这些值在编译时立即得知,因此它会为您添加,从而产生文字字符串"Hello"
。 因此,它具有与s1
和s2
相同的值,因此每个都将引用相同的实例。
s7
不能以同样的方式简化。 s7
最初以"He"
开头。 这里的区别是s7 = s7.concat("llo");
将s7
重新分配给函数调用的结果。 这不能简化。 就Java编译器而言,所有函数调用的结果在编译时都是未知的。 由于它不知道结果值,因此不能简化并保持不变。 结果调用返回"Hello"
字符串的新实例,该字符串与编译时实例( s8
共享)的实例不同。
s10
也不能以同样的方式简化。 s10
最初以"He"
开头。 然后重新分配s10 = s10 + "llo";
这无法简化。 你可能会问为什么? 那么s10
是非最终变量表达式。 从技术上讲,编译器不知道它的值,因为它不是常量。 如果s10
被声明为final String
,那么这可以是常量折叠(当分配给不同的变量时)。
因此,请考虑此版本的测试代码:
public static void main(String[] args)
{
String s1 = "Hello";
String s2 = "Hello";
System.out.println("1: s1 == s2 " + (s1 == s2)); // 1
String s3 = "Hel" + "lo";
String s4 = "Hel".concat("lo");
System.out.println("2: s1 == s3 " + (s1 == s3)); // 2
System.out.println("3: s1 == s4 " + (s1 == s4)); // 3
String he = "He";
String s5 = he + "llo";
String s6 = he.concat("llo");
System.out.println("4: s1 == s5 " + (s1 == s5)); // 4
System.out.println("5: s1 == s6 " + (s1 == s6)); // 5
final String fhe = "He";
String s7 = fhe + "llo";
String s8 = fhe.concat("llo");
System.out.println("6: s1 == s7 " + (s1 == s7)); // 6
System.out.println("7: s1 == s8 " + (s1 == s8)); // 7
}
你能弄清楚哪条线是真的吗?
true,true,false,false,false,true,false
你可能想知道为什么3和7不是真的。 简而言之,Java编译器没有编程
要足够智能以识别concat()调用,因此将其视为常规函数调用。
equals运算符测试引用是否相同(即指向同一对象),而不是引用的值是否相同。 如果您需要测试一个字符串是否等于另一个字符串,则应使用内置的.equals方法。 这将进行对象值比较。 例如
final String expectedValue = "Foo";
final String actualValue = "F" + "oo";
if (expectedValue.equals(actualValue)) {
// This will trigger where == would not
}
另外为了安全起见,如果你比较两个字符串而另一个是常量,通常最好在常量上调用equals,即
String myValue = getMyValue;
boolean theSame = "Some value".equals(myValue);
而不是
String myValue = getMyValue;
boolean theSame = myValue.equals("Some Value");
原因是第二种形式存在空指针异常的风险,可以通过在保证存在的常量字符串上调用equals()来避免这种情况。
你不能对字符串对象做任何假设。
VM可以努力确保不会同时存在包含完全相同的char数组的两个字符串对象,而其他VM将允许重复。
==
运算符仅检查两个对象是否具有相同的地址(指针)。 仅对于不是引用的原始类型(如int,char等),它才会比较该值。
您需要使用类似s1.equals(s2)
的内容来比较两个字符串的内容。
在您提供的示例中,这是正在发生的事情:
String s7 = “He”; //s7 is an object referencing a part of memory holding “He”
String s8 = “Hello”; //s8 is an object referencing a part of memory holding “Hello”
s7 = s7.concat(“llo”); //s7.concat(“llo”) created a new object in memory that contains “Hello” and s7 now references this now object
(s7==s8) //checks if the equality of the object reference and this is false since they reference different memory addresses.
(s7.equals(s8)) //this will compare s7 and s8 however way the String class compares two String objects.
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.