Difference Between == and equals() in Java – Java中==和equals()的区别

最后修改: 2022年 5月 5日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll describe two basic equality checks in Java – reference equality and value equality. We’ll compare them, show examples, and highlight the key differences between them.

在本教程中,我们将描述Java中两种基本的平等检查–引用平等和值平等。我们将对它们进行比较,展示实例,并强调它们之间的主要区别。

Also, we’ll focus on null checks and understand why we should use reference equality instead of value equality when working with objects.

此外,我们还将关注null检查,并理解为什么我们在处理对象时应该使用引用相等而不是值相等。

2. Reference Equality

2.参考文献 平等

We’ll start by understanding reference comparison, which is represented by the equality operator (==). Reference equality occurs when two references point to the same object in the memory.

我们将从理解引用比较开始,它由平等运算符(==)表示。当两个引用指向内存中的同一个对象时,就会出现引用相等的情况

2.1. Equality Operator With Primitive Types

2.1.带有原始类型的等价运算符

We know that the primitive types in Java are simple, non-class raw values. When we use the equality operator with primitive types, we’re just comparing their values:

我们知道,Java 中的原始类型是简单的、非类的原始值。当我们对原始类型使用平等运算符时,我们只是在比较它们的值。

int a = 10;
int b = 15;
assertFalse(a == b);

int c = 10;
assertTrue(a == c);

int d = a;
assertTrue(a == d);

As shown above, equality and reference checking work identically for primitives. When we initialize a new primitive with the same value, the check returns true. Moreover, the operator returns the same result if we reassign the origin value to the new variable and compare it.

如上所示,等价和引用检查对基元的作用是相同的。当我们用相同的值初始化一个新的基元时,检查返回真。此外,如果我们将原值重新赋值给新的变量并进行比较,操作符会返回相同的结果。

Let’s now perform null checks:

现在我们来执行null检查。

int e = null; // compilation error
assertFalse(a == null); // compilation error
assertFalse(10 == null); // compilation error

Java prohibits assigning null to a primitive. In general, we can’t perform any null checks with the equality operator on primitive variables or values.

Java禁止将null分配给一个基元。一般来说,我们不能用平等运算符对原始变量或值进行任何null检查。

2.2. Equality Operator With Object Types

2.2.带有对象类型的等价操作符

As for object types in Java, the equality operator performs a referential equality comparison only, ignoring the object values. Before we implement the tests, let’s create a simple custom class:

至于Java中的对象类型平等运算符仅执行参考性平等比较,忽略对象的值。在我们实现测试之前,让我们创建一个简单的自定义类。

public class Person {
    private String name;
    private int age;

    // constructor, getters, setters...
}

Now, let’s initialize some class objects and inspect the equality operator results:

现在,让我们初始化一些类对象并检查平等运算符的结果。

Person a = new Person("Bob", 20);
Person b = new Person("Mike", 40);
assertFalse(a == b);

Person c = new Person("Bob", 20);
assertFalse(a == c);

Person d = a;
assertTrue(a == d);

The results are quite different than before. The second check returns false while we had got true for the primitives. As we mentioned earlier, the equality operator ignores the internal values of the object when comparing. It only checks that two variables are referencing the same memory address.

结果与之前有很大不同。第二次检查返回false,而我们在基元中得到的是true。正如我们前面提到的,平等运算符在比较时忽略了对象的内部值。它只是检查两个变量是否引用了相同的内存地址

Unlike primitives, we can use nulls while working with objects:

与基元不同,我们在处理对象时可以使用nulls。

assertFalse(a == null);<br/>
Person e = null;<br/>
assertTrue(e == null);

assertFalse(a == null);<br/>
Person e = null;<br/>
assertTrue(e == null);

By using the equality operator and comparing null, we check if the object assigned to the variable is already initialized.

通过使用平等运算符和比较null,我们检查分配给变量的对象是否已经被初始化

3. Value Equality

3.价值平等

Let’s now focus on the value equality test. Value equality takes place when two separate objects happen to have the same values or state.

现在让我们来关注一下价值平等测试。当两个独立的对象碰巧具有相同的值或状态时,就会发生价值平等。

This compares values and is closely related to the Object’s equals() method. As before, let’s compare its use with primitives and object types, looking at key differences.

这是对值的比较,与Object的equals()方法密切相关。像以前一样,让我们比较一下它与基元和对象类型的使用,看看关键的区别。

3.1. equals() Method With Primitive Types

3.1.带有原始类型的equals()方法

As we know, primitives are basic types with a single value and don’t implement any methods. Therefore, it’s impossible to call the equals() method directly using primitives:

正如我们所知,基元是具有单一值的基本类型,并且没有实现任何方法。因此,使用基元不可能直接调用equals()方法

int a = 10;<br/>
assertTrue(a.equals(10)); // compilation error

int a = 10;<br/>
assertTrue(a.equals(10)); // 编译错误

However, since every primitive has its own wrapper class, we can use the boxing mechanism to cast it into its object representation. Then, we can easily call the equals() method as if we are using object types:

然而,由于每个基元都有自己的封装类,我们可以使用boxing机制将其投递到其对象表示中。然后,我们可以轻松地调用equals()方法,就像我们在使用对象类型一样。

int a = 10;
Integer b = a;

assertTrue(b.equals(10));

3.2. equals() Method With Object Types

3.2.带有对象类型的equals()方法

Let’s go back to our Person class. For the equals() method to work correctly, we need to override the method in the custom class by considering the fields contained in the class:

让我们回到我们的Person类。为了使equals()方法正常工作,我们需要在自定义类中通过考虑类中包含的字段来覆盖该方法

public class Person {
    // other fields and methods omitted

    @Override
    public boolean equals(Object o) {
        if (this == o) 
            return true;
        if (o == null || getClass() != o.getClass()) 
            return false;
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }
}

First of all, the equals() method returns true if the given value has the same reference, which is checked by the reference operator. If not, we start the equality test.

首先,equals()方法返回true,如果给定的值有相同的引用,这将由引用操作符检查。如果不是,我们就开始进行平等测试。

Further, we test the equality of the Class objects for both values. We return false if they’re different. Otherwise, we continue checking for equality. Finally, we return the combined result of comparing each property separately.

此外,我们测试两个值的对象是否相等。如果它们不同,我们就返回false。否则,我们继续检查是否相等。最后,我们返回分别比较每个属性的综合结果。

Now, let’s modify the previous test and check the results:

现在,让我们修改之前的测试并检查结果。

Person a = new Person("Bob", 20);
Person b = new Person("Mike", 40);
assertFalse(a.equals(b));

Person c = new Person("Bob", 20);
assertTrue(a.equals(c));

Person d = a;
assertTrue(a.equals(d));

As we can see, the second check returns true as opposed to the reference equality. Our overridden equals() method compares the internal values of the objects.

我们可以看到,相对于引用平等,第二个检查返回true。我们重载的equals()方法比较了对象的内部值。

If we don’t override the equals() method, the method from the parent class Object is used. Since the Object.equals() method only does reference equality check, the behavior might not be what we’d expect when comparing Person objects.

如果我们没有覆盖equals()方法,就会使用来自父类Object的方法。由于Object.equals()方法只做引用的平等性检查,所以在比较Person对象时,行为可能不是我们所期望的。

While we haven’t shown the hashCode() method above, we should note that it’s important to override it whenever we override the equals() method to ensure consistency between these methods.

虽然我们没有在上面展示hashCode()方法,但我们应该注意到,每当我们覆盖equals()方法时,必须覆盖它以确保这些方法的一致性

4. Null Equality

4.空洞的平等性

In the end, let’s check, how the equals() method works with the null value:

最后,让我们检查一下,equals()方法对null值的作用。

Person a = new Person("Bob", 20);
Person e = null;
assertFalse(a.equals(e));
assertThrows(NullPointerException.class, () -> e.equals(a));

When we check it using the equals() method against the other object, we get two different results depending on the order of those variables. The last statement throws an exception because we call the equals() method on the null reference. To fix the last statement, we should first call the equality operator check:

当我们用equals()方法对另一个对象进行检查时,我们得到两个不同的结果,这取决于这些变量的顺序。最后一条语句抛出了一个异常,因为我们对null引用调用了equals()方法。为了解决最后一条语句,我们应该首先调用平等运算符检查。

assertFalse(e != null && e.equals(a));

Now, the left side of the condition returns false, making the entire statement equal to false, preventing the NullPointerException from being thrown. Therefore, we must remember to first check that the value on which we are calling the equals() method is not null, otherwise, it can lead to annoying bugs.

现在,条件的左边返回false,使整个语句等于false,防止抛出NullPointerException。因此,我们必须记住首先检查我们调用equals()方法的值是不是null,否则,会导致恼人的错误。

Moreover, since Java 7, we can use a null-safe Objects#equals() static method to perform equality checks:

此外,从Java 7开始,我们可以使用一个空安全的Objects#equals() 静态方法来执行平等检查。

assertFalse(Objects.equals(e, a));
assertTrue(Objects.equals(null, e));

This helper method performs additional checks to prevent throwing the NullPointerException, returning true when both parameters are null.

这个辅助方法执行额外的检查以防止抛出NullPointerException,当两个参数都是null时返回true

5. Conclusion

5.总结

In this article, we discussed reference equality and value equality checks against primitive and object values.

在这篇文章中,我们讨论了针对基元和对象值的引用平等和值平等 检查。

To test for the reference equality, we use the == operator. This operator works slightly differently for primitive values ​​and objects. When we use the equality operator with primitives, it compares values. On the other hand, when we use it with for objects, it checks memory references. By comparing it with a null value, we simply check that the object is initialized in memory.

为了测试引用是否相等,我们使用==运算符。这个运算符对基元值和对象的工作方式略有不同。当我们对基元使用平等运算符时,它比较的是值。另一方面,当我们对对象使用时,它检查内存引用。通过与null值进行比较,我们简单地检查该对象是否在内存中被初始化。

To perform a value equality test in Java, we use the equals() method inherited from Object. Primitives are simple non-class values, so this method cannot be called without wrapping.

为了在Java中进行值的平等性测试,我们使用从Object继承的equals()方法。基元是简单的非类值,所以这个方法在没有包装的情况下不能被调用。

We also need to remember to only call the equals() method on an instantiated object. Otherwise, an exception will be thrown. To prevent this, if we suspect a null value, we should check the value with the == operator.

我们还需要记住,只在一个实例化的对象上调用equals()方法。否则,将抛出一个异常。为了防止这种情况,如果我们怀疑有一个null值,我们应该用==操作符检查该值。

As always, the source code for the examples is available over on GitHub.

像往常一样,这些例子的源代码可以在GitHub上找到