Java中的“==”和“equals()”有什么区别?

768

我想澄清一下,如果我理解正确的话:

  • == 是引用比较,即两个对象指向同一内存位置
  • .equals() 则比较对象中的值

62
差不多,没错。 - John Kane
13
可以,.equals() 可以被理解为“有实际意义的等价”。 - vikingsteve
可能是如何在Java中比较字符串?的重复问题。 - TylerH
32
“both objects point to the same memory location”这样的语句表达不够精确,会导致理解困难。您的意思是:“两个变量引用同一个对象”。请注意,变量不是对象;变量是指向对象的引用。 对象不会“指向”任何东西。 - Jesper
1
在C#(以及许多其他语言)中,相等运算符(**==)对应于Object.Equals()方法。派生类,如String,可以通过重写.Equals方法来定义两个字符串是==的含义。Java无法这样做。Java String类(以及Java中的任何类)都没有覆盖==**以使其行为符合预期的方法。这意味着你必须自己手动调用.equals() - Ian Boyd
"==" 用于原始类型,而 ".equals()" 用于比较引用类型(如果在引用类型上使用 "==",则默认比较哈希码)。 - Rishon_JR
26个回答

737

一般而言,对于你的问题,答案是“是”,但是...

  • .equals(...)方法只会比较它所指定的内容,不多不少。
  • 如果一个类没有覆盖equals方法,那么它将默认使用最近的已重写此方法的父类的equals(Object o)方法。
  • 如果没有父类提供覆盖,则默认使用最终父类Object的方法,因此您将得到Object#equals(Object o)方法。根据Object API,这与==相同;也就是说,它仅在两个变量引用同一个对象时返回true。因此,您将测试对象相等性而不是功能相等性
  • 始终记得在覆盖equals时覆盖hashCode,以免“违反契约”。根据API,对于equals方法显示它们等效的两个对象,hashCode()方法返回的结果必须相同。反之则不一定成立。

如果 == 检查内存引用,那么为什么我在[这里][1]得到了奇怪的行为:https://docs.google.com/document/d/1ducE-yZEDyyjoKUTkTEsw3sdSw740d0Z5iiJz3IzABE/edit?usp=sharing 我期望的输出是 true。请帮我澄清疑惑。 - JPG
4
打印d1和d2的值,你会发现为什么会返回false。 - BoDidely
2
@BoDidely我解决了。这是因为所有的包装类都是不可变的。 - JPG
类Object的equals方法实现了对象上最具有区分性的等价关系;也就是说,对于任何非空引用值x和y,当且仅当x和y引用同一个对象(x == y的值为true)时,该方法返回true。 请注意,通常需要重写hashCode方法,每当重写此方法时,以维护hashCode方法的一般契约,即相等的对象必须具有相等的哈希码。 - Abhijeet
不相关的内容:今天我发布了一个元问题(https://meta.stackoverflow.com/questions/372795/are-these-good-auto-messages),关于在评论低质量新手问题时使用好/有效/...“自动”消息。我收到的反馈感觉非常“你正在做完全错误的事情”。现在我只是想知道你对此怎么看?你是否有“通用”的消息,还是在这种情况下仅编写特定的评论? - GhostCat

137

关于 String 类:

equals() 方法 比较堆中 String 实例内部的 "value",无论两个对象引用是否指向同一个 String 实例。如果任意两个类型为 String 的对象引用指向同一个 String 实例,则非常好!如果两个对象引用指向两个不同的 String 实例... 这并没有什么区别。被比较的是每个 String 实例内部的 "value"(也就是字符数组的内容)。

另一方面,"==" 运算符 比较 两个对象引用 的值,以确定它们是否引用同一个 String 实例。 如果这两个对象引用的值都“引用”同一个 String 实例,那么布尔表达式的结果将是“true”...显然。 如果,另一方面,这两个对象引用的值都“引用”不同的 String 实例(即使这两个 String 实例具有相同的“value”,也就是每个 String 实例的字符数组内容相同),布尔表达式的结果将是“false”。

就像任何解释一样,请理解一下。

希望这能让事情更加清晰明了。


1
所以对于字符串来说,== 也是引用相等吗?也就是说它和其他对象一样工作吗? - JonnyRaa
3
(我知道这是个旧帖了...)对于String类型,==也是引用相等的,但通常情况下它会起作用(例如两个具有相同内容的String通常会相互==),因为Java如何处理String。它并不总是有效的,而且这肯定是一种不好的做法,但这是一个常见的错误,特别是来自其他语言的人。 - Tonio
14
Tonio的评论提到,由字符串字面值构建的String将被添加到称为String常量池的东西中,例如 String s1 = "someString"; String s2 = "someString;"s1s2将共享相同的引用。s1 == s2将返回true。但是,如果它们是通过String构造函数构造的,例如String s1 = new String("someString"); String s2 = new String("someString");,则它们将不共享相同的引用。s1 == s2将返回false。 - Gavin

75

如果你在讨论“原始类型”或“对象类型”,那么有一些小差异;同样,如果你在讨论“静态”或“非静态”成员,也可以这样说;你还可以混合上述所有内容...

这里有一个例子(你可以运行它):

public final class MyEqualityTest
{
    public static void main( String args[] )
    {
        String s1 = new String( "Test" );
        String s2 = new String( "Test" );

        System.out.println( "\n1 - PRIMITIVES ");
        System.out.println( s1 == s2 ); // false
        System.out.println( s1.equals( s2 )); // true

        A a1 = new A();
        A a2 = new A();

        System.out.println( "\n2 - OBJECT TYPES / STATIC VARIABLE" );
        System.out.println( a1 == a2 ); // false
        System.out.println( a1.s == a2.s ); // true
        System.out.println( a1.s.equals( a2.s ) ); // true

        B b1 = new B();
        B b2 = new B();

        System.out.println( "\n3 - OBJECT TYPES / NON-STATIC VARIABLE" );
        System.out.println( b1 == b2 ); // false
        System.out.println( b1.getS() == b2.getS() ); // false
        System.out.println( b1.getS().equals( b2.getS() ) ); // true
    }
}

final class A
{
    // static
    public static String s;
    A()
    {
        this.s = new String( "aTest" );
    }
}

final class B
{
    private String s;
    B()
    {
        this.s = new String( "aTest" );
    }

    public String getS()
    {
        return s;
    }

}

你可以通过这些链接比较"=="(等号操作符)和".equals(...)"(java.lang.Object类中的方法)的解释:


2
有趣的例子。不同于以上回答的观点。谢谢! - Andrew
1
在我看来,这是最好的答案,因为它比其他全文回答更清晰,而且没有失去解释(当然,如果你理解类和静态概念的话)。 - Carrm

54
==equals之间的区别让我困惑了一段时间,直到我决定仔细研究它。很多人说比较字符串应该使用equals而不是==。希望在这个答案中我能够说明两者的差异。
回答这个问题的最好方法是通过自问几个问题来解决。所以让我们开始吧:
下面程序的输出是什么:TBD
String mango = "mango";
String mango2 = "mango";
System.out.println(mango != mango2);
System.out.println(mango == mango2);

如果你说,

false
true

我觉得你是正确的,但是你为什么这样说呢? 如果你说输出结果为,

true
false

我会说你是错误的,但我还是会问你,为什么你认为那是正确的?

好的,让我们尝试回答这个问题:

以下程序的输出是什么:

String mango = "mango";
String mango3 = new String("mango");
System.out.println(mango != mango3);
System.out.println(mango == mango3);

现在,如果你说:

false
true

我会说你是错误的,但是为什么现在它是错误的呢?这个程序的正确输出是:

true
false

请比较上述程序并思考。

好的。现在这可能会有所帮助(请阅读此内容:打印对象地址 - 虽然不可能,但我们仍然可以使用它)。

String mango = "mango";
String mango2 = "mango";
String mango3 = new String("mango");
System.out.println(mango != mango2);
System.out.println(mango == mango2);
System.out.println(mango3 != mango2);
System.out.println(mango3 == mango2);
// mango2 = "mang";
System.out.println(mango+" "+ mango2);
System.out.println(mango != mango2);
System.out.println(mango == mango2);
 
System.out.println(System.identityHashCode(mango));
System.out.println(System.identityHashCode(mango2));
System.out.println(System.identityHashCode(mango3));

你能试着想一想上面代码中最后三行的输出结果吗? 对于我来说,ideone打印出了这个结果(你可以在这里检查代码):

false
true
true
false
mango mango
false
true
17225372
17225372
5433634

哦!现在你看到了mangomango2identityHashCode()值相等,但它与mango3的不同。

尽管所有字符串变量——mango、mango2mango3——都具有相同的值,即“mango”,但identityHashCode()仍然不是所有变量的相同。

现在试着取消注释这行代码 // mango2 = "mang"; 然后再次运行程序,你会发现所有三个identityHashCode()都不同。嗯,这是一个有用的提示。

我们知道如果hashcode(x) = Nhashcode(y) = N => x = y

我不确定Java内部是如何工作的,但我认为这就是当我说:

mango = "mango";

Java创建了一个字符串"mango",并且这个字符串被一个变量mango所引用。

mango ----> "mango"

现在,在下一行中,我说:

mango2 = "mango";

实际上,它重复使用了相同的字符串"mango",看起来像这样。

mango ----> "mango" <---- mango2

两个变量mango和mango2指向同一个引用。 现在,当我说:

mango3 = new String("mango")

它实际上为“mango”创建了一个全新的引用(字符串),大致看起来是这样的:

mango -----> "mango" <------ mango2

mango3 ------> "mango"

因此,当我输出mango == mango2的值时,它输出了true。而当我输出mango3 == mango2的值时,尽管两个变量的值相同,但它输出了false

当你取消注释// mango2 = "mang";这行时,它实际上创建了一个字符串"mang",并将我们的图形变成了这样:

mango ---->"mango"
mango2 ----> "mang"
mango3 -----> "mango"

这就是为什么identityHashCode不会对所有对象都相同。

希望这能对你们有所帮助。 事实上,我想生成一个测试用例,其中==失败,但equals()成功。 请随时评论并让我知道如果我错了。


mango == mango2 发生的原因是因为你没有将 mango2 创建为一个新的字符串对象,而是直接引用了 "mango" - brt
1
使用String类型作为比较==和equals()的例子是错误的,因为当String类型没有使用new关键字时,它们会被放入字符串池中,而每当相同的字符串被分配给新的引用时,它指向池中的同一字符串。因此,最好使用一些自定义对象的例子来进行==和.equals()的比较,以消除疑虑。 - om252345

34
== 运算符测试两个变量是否具有相同的引用(也称指向内存地址的指针)。
String foo = new String("abc");
String bar = new String("abc");

if(foo==bar)
// False (objects are identical but not same)

bar = foo;

if(foo==bar)
// True (Now, objects are identical and same)

而equals()方法则用于测试两个变量是否引用了具有相同状态(值)的对象。
String foo = new String("abc");
String bar = new String("abc");

if(foo.equals(bar))
// True (The objects are identical but not same)

1
错误。如果(foo==bar),这应该是真的,而不是假的。它将重用相同的字符串“adc”。在沙盒中测试它,它将返回两个真。 - Johnathan Logan
2
@JohnathanLogan 我猜这是由于字符串内部化引起的。现在我已经改成了 "new String("abc")"。希望现在不会有任何问题。谢谢你的提醒。 - Mohan
你的回答仅适用于String对象,因为String覆盖了equals方法,如果“表示相同的字符序列”,则返回true。https://docs.oracle.com/javase/7/docs/api/java/lang/String.html对于原始问题的一般情况,你的回答要么是错误的,要么是误导性的,要么是不正确的。 - K. hervey

15
你将需要覆盖equals函数(以及其他函数),才能将其与自定义类一起使用。
equals方法用于比较对象。
==二元运算符用于比较内存地址。

14

==是一个运算符equals()是一个方法

运算符通常用于比较原始类型,因此==用于内存地址比较,而equals()方法用于比较对象


1
简单而最容易的答案 - Gulab Sagevadiya

9
 String w1 ="Sarat";
 String w2 ="Sarat";
 String w3 = new String("Sarat");

 System.out.println(w1.hashCode());   //3254818
 System.out.println(w2.hashCode());   //3254818
 System.out.println(w3.hashCode());   //3254818

 System.out.println(System.identityHashCode(w1)); //prints 705927765
 System.out.println(System.identityHashCode(w2)); //prints 705927765
 System.out.println(System.identityHashCode(w3)); //prints 366712642


 if(w1==w2)   //  (705927765==705927765)
 {
   System.out.println("true");
 }
 else
 {
   System.out.println("false");
 }
 //prints true

 if(w2==w3)   //  (705927765==366712642)
 {
   System.out.println("true");
 }
 else
 {
   System.out.println("false");
 }
 //prints false


 if(w2.equals(w3))   //  (Content of 705927765== Content of 366712642)
 {
   System.out.println("true");
 }
 else
 {
   System.out.println("false");
 }
 //prints true

简单而最佳的解释 - Sritam Jagadev

8

如果你没有重写.equals()方法,那么==和.equals()都指向同一个对象。

一旦你重写了.equals()方法,你可以根据需要比较调用对象的状态和传递对象的状态,或者只需调用super.equals()。


6

以下是区分关系运算符==.equals()方法的一般性规则。

object1 == object2比较的是object1和object2指向的内存中是否为同一地址

object1.equals(object2)比较的是不考虑它们在内存中的位置,而是比较它们的值

可以使用String来很好地演示这一点。

情况1

 public class Conditionals {

    public static void main(String[] args) {
       String str1 = "Hello";
       String str2 = new String("Hello");
       System.out.println("is str1 == str2 ? " + (str1 == str2 ));
       System.out.println("is str1.equals(str2) ? " + (str1.equals(str2 )));
    }

 }



The result is
      is str1 == str2 ? false
      is str1.equals(str2) ? true 

场景2

public class Conditionals {

    public static void main(String[] args) {
       String str1 = "Hello";
       String str2 = "Hello";
       System.out.println("is str1 == str2 ? " + (str1 == str2 ));
       System.out.println("is str1.equals(str2) ? " + (str1.equals(str2 )));
    }

}

The result is 
  is str1 == str2 ? true
  is str1.equals(str2) ? true

这种字符串比较可以作为比较其他类型对象的基础。

例如,如果我有一个人类,我需要定义用于比较两个人的标准。假设这个人类具有身高和体重的实例变量

因此,创建人物对象person1和person2,并使用.equals()比较这两个人时,我需要覆盖person class的equals方法,以定义基于哪些实例变量(身高或体重)进行比较。

但是,==运算符仍将返回基于两个对象(person1和person2)的内存位置的结果

为了方便通用化比较该人物对象,我创建了以下测试类。对这些概念进行实验将揭示大量事实

package com.tadtab.CS5044;

public class Person {

private double height;
private double weight;

public double getHeight() {
    return height;
}

public void setHeight(double height) {
    this.height = height;
}

public double getWeight() {
    return weight;
}

public void setWeight(double weight) {
    this.weight = weight;
}


@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    long temp;
    temp = Double.doubleToLongBits(height);
    result = prime * result + (int) (temp ^ (temp >>> 32));
    return result;
}

@Override
/**
 * This method uses the height as a means of comparing person objects.
 * NOTE: weight is not part of the comparison criteria
 */
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Person other = (Person) obj;
    if (Double.doubleToLongBits(height) != Double.doubleToLongBits(other.height))
        return false;
    return true;
}

public static void main(String[] args) {
    
    Person person1 = new Person();
    person1.setHeight(5.50);
    person1.setWeight(140.00);
    
    Person person2 = new Person();
    person2.setHeight(5.70);
    person2.setWeight(160.00);
    
    Person person3 = new Person();
    person3 = person2;
    
    Person person4 = new Person();
    person4.setHeight(5.70);
    
    Person person5 = new Person();
    person5.setWeight(160.00);
    
    System.out.println("is person1 == person2 ? " + (person1 == person2)); // false;
    System.out.println("is person2 == person3 ? " + (person2 == person3)); // true 
    //this is because perosn3 and person to refer to the one person object in memory. They are aliases;
    System.out.println("is person2.equals(person3) ? " + (person2.equals(person3))); // true;
    
    System.out.println("is person2.equals(person4) ? " + (person2.equals(person4))); // true;
    
    // even if the person2 and person5 have the same weight, they are not equal.
    // it is because their height is different
    System.out.println("is person2.equals(person4) ? " + (person2.equals(person5))); // false;
}

}

这个类的执行结果是:

is person1 == person2 ? false
is person2 == person3 ? true
is person2.equals(person3) ? true
is person2.equals(person4) ? true
is person2.equals(person4) ? false

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接