繁体   English   中英

关于java中==运算符的问题

[英]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" ; 它足够聪明地弄清楚它们是同一个东西。

其他的很复杂,无法对它们进行优化,因此您最终会得到单独的对象。

==不检查位模式,它将比较对象的内存地址。 只有同一个对象具有相同的内存地址。

克林特的答案很好,但我会进一步扩展它并在编译器级解释。

如您所知, s1s2将最终成为对同一字符串实例"Hello"引用。

对于s5s6 ,编译器会看到常量表达式。 也就是说,它看到两个常量(字符串文字)之间的操作。 编译器知道如何添加字符串以及结果是什么。 由于这些值在编译时立即得知,因此它会为您添加,从而产生文字字符串"Hello" 因此,它具有与s1s2相同的值,因此每个都将引用相同的实例。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM