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

时间:2010-09-26 19:31:49

标签: java string operator-keyword equals

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给出错误。有人可以解释一下,在s7 = s7.concat(“llo”)中实际发生了什么;并且s10 = s10 +“llo”;我理解==运算符检查引用和equal()检查对象的内容。但我需要知道为什么s7和s10参考变量位模式与s8和s1不同。如果这些事情与编译时生成的字符串和运行时生成的字符串有关,那么如何识别它是编译时还是运行时字符串?

7 个答案:

答案 0 :(得分:11)

之所以发生这种情况,是因为Java在编译器中进行了优化。当它看到您将文字字符串"Hello"分配给s1时,它对s2使用相同的“Hello”,因为所有Java String操作都是非破坏性的(例如,它们返回克隆而不是修改原始字符串) ,这样做是安全的。

同样适用于"Hel" + "lo" vs "He" + "llo";它足够巧妙地弄清楚它们是一样的。

其他内容非常复杂,无法对其进行优化,因此您最终会得到单独的对象。

答案 1 :(得分:4)

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

答案 2 :(得分:4)

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

如您所知,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()调用,因此被视为常规函数调用。

答案 3 :(得分:0)

equals运算符测试引用是否相同(即指向同一对象),而不是引用的值是否相同。如果您需要测试一个字符串是否等于另一个字符串,则应使用内置的.equals方法。这将进行对象值比较。 e.g。

final String expectedValue = "Foo";
final String actualValue = "F" + "oo";
if (expectedValue.equals(actualValue)) {
  // This will trigger where == would not
}

另外为了安全起见,如果你比较两个字符串而一个是常量,通常最好在常量上调用等号,即

String myValue = getMyValue;
boolean theSame = "Some value".equals(myValue);

而不是

String myValue = getMyValue;
boolean theSame = myValue.equals("Some Value");

原因是第二种形式存在空指针异常的风险,可以通过在保证存在的常量字符串上调用equals()来避免这种情况。

答案 4 :(得分:0)

你不能对字符串对象做任何假设。

VM可以努力确保不会同时存在包含完全相同的char数组的两个字符串对象,而其他VM将允许重复。

答案 5 :(得分:0)

==运算符仅检查两个对象是否具有相同的地址(指针)。仅适用于不是引用的原始类型(如int,char等),它会比较该值。

您需要使用s1.equals(s2)之类的内容来比较两个字符串的内容。

答案 6 :(得分:0)

在您提供的示例中,这是正在发生的事情:

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.