【问题标题】:Question about == operator in java关于java中==运算符的问题
【发布时间】:2011-04-17 11:59:42
【问题描述】:
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 不同。如果这些东西与编译时生成的字符串和运行时生成的字符串有关,那么我如何识别它是编译时字符串还是运行时字符串?

【问题讨论】:

  • “引用变量位模式”到底是什么意思?
  • 另一个关于字符串问题的“==”。似乎经常有人问到这个问题的变体。难道不应该有一个标准答案,并且所有后续出现的都作为重复项关闭吗?
  • @cameron 我从某本书中了解到,如果引用变量 c 引用对象 X017432 并且引用变量 d 也引用对象 X017432,那么 c 和 d 中的位是相同的。所以我需要知道为什么 s7 和 s10 引用不等于 s8 和 s10 已经引用了字符串池中的“Hello”。

标签: java string operator-keyword equals


【解决方案1】:

Clint 的回答很好,但我会进一步扩展并在编译器级别进行解释。

如您所知,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
}

你能找出哪些是真的吗?

真、真、假、假、假、真、假
您可能想知道为什么 3 和 7 不正确。简短的回答,Java 编译器没有被编程
足够聪明以识别 concat() 调用,因此被视为常规函数调用。

【讨论】:

  • 谢谢你,但我有些怀疑你为什么说 s7.concat("llo");不能简化。这是否意味着 concat 方法不会在编译时生成“Hello”字符串?如果是,那么它会在池中创建新对象(Hello)而不是现有的“Hello”对象吗?
  • 啊抱歉,我以为我解释的比实际写的要多得多。我会详细说明。
【解决方案2】:

在您提供的示例中,情况如下:

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. 

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

        【解决方案5】:

        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() 来避免。

        【讨论】:

          【解决方案6】:

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

          【讨论】:

          • 那是我原来的答案,不是他要问的。
          • @Clint Tseng:嘿,你说得对,我的错。像马克一样感到困惑,并认为它与平等有关:p
          【解决方案7】:

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

          "Hel" + "lo""He" + "llo" 也是如此;很聪明地发现它们是同一个东西。

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

          【讨论】:

          猜你喜欢
          • 2011-01-08
          • 1970-01-01
          • 1970-01-01
          • 2013-08-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-11-22
          • 1970-01-01
          相关资源
          最近更新 更多