What is the Difference between '==' and 'equals()' in Java?

In Java, you often encounter situations where you need to compare objects. Two commonly used methods for comparison are the == operator and the equals() method. However, these two mechanisms differ significantly in their behavior. This blog post aims to clarify the differences between == and equals() through practical examples. We’ll explore ten scenarios that illustrate when to use each of these comparison techniques.

Prerequisites

If you don’t already have Maven installed, you can download it from the official Maven website https://maven.apache.org/download.cgi or through SDKMAN https://sdkman.io/sdks#maven

You can clone the https://github.com/dmakariev/examples repository.

git clone https://github.com/dmakariev/examples.git
cd examples/java-core/equals

Creating a Maven Project

Let’s create a our project

  1. Open your terminal and navigate to the directory where you want to create your project.
  2. Run the following command to generate a new Maven project:
    mvn archetype:generate -DgroupId=com.makariev.examples.core -DartifactId=equals -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
    

    This command generates a basic Maven project structure with a sample Java class, and the group ID and artifact ID are set as per your requirements.

Deleting Initial Files and Updating Dependencies

To clean up the initial files generated by the Maven archetype and update dependencies, follow these steps:

  1. Delete the src/main/java/com/makariev/examples/core/App.java file.
  2. Delete the src/test/java/com/makariev/examples/core/AppTest.java file.
  3. Open the pom.xml file and delete the JUnit 3 dependency (junit:junit).
  4. Add the JUnit 5 and AssertJ dependencies to the pom.xml file:
<dependencies>
    <!-- JUnit 5 -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.10.0</version> <!-- Use the latest version -->
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <version>5.10.0</version> <!-- Use the latest version -->
        <scope>test</scope>
    </dependency>
    <!-- AssertJ -->
    <dependency>
        <groupId>org.assertj</groupId>
        <artifactId>assertj-core</artifactId>
        <version>3.24.2</version> <!-- Use the latest version -->
        <scope>test</scope>
    </dependency>
</dependencies>

Ten == vs. equals() Examples

We’ll now explore ten different examples to illustrate the differences between == and equals().

1. Comparing Primitives with ==

int x = 5;
int y = 5;
boolean result = (x == y); // true

2. Comparing Strings with ==

String str1 = "Hello";
String str2 = "Hello";
boolean result = (str1 == str2); // true (due to string interning)

3. Comparing Strings with equals()

String str1 = new String("Hello");
String str2 = new String("Hello");
boolean result = str1.equals(str2); // true (contents are the same)

4. Comparing Objects with ==

Object obj1 = new Object();
Object obj2 = obj1;
boolean result = (obj1 == obj2); // true (same reference)

5. Comparing Objects with equals()

String s1 = new String("Hello");
String s2 = new String("Hello");
boolean result = s1.equals(s2); // true (contents are the same)

6. Custom Class Comparison with ==

class Person {
    private String name;
    Person(String name) { this.name = name; }
}
Person person1 = new Person("Alice");
Person person2 = person1;
boolean result = (person1 == person2); // true (same reference)

7. Custom Class Comparison with equals()

7.1 The equals() should be overridden

class Person {
    private String name;
    Person(String name) { this.name = name; }
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Person person = (Person) obj;
        return name.equals(person.name);
    }
}
Person person1 = new Person("Alice");
Person person2 = new Person("Alice");
boolean result = person1.equals(person2); // true (contents are the same)

7.2 if the equals() is not overridden, then it falls back to comparing instances (as if == is used )

class PersonNoEquals {
    private String name;
    Person(String name) { this.name = name; }
}
PersonNoEquals person1 = new PersonNoEquals("Alice");
PersonNoEquals person2 = new PersonNoEquals("Alice");
boolean result1 = person1.equals(person2);//false (different reference, no 'equals()')
boolean result2 = person1 == person2;//false (different reference)

PersonNoEquals person3 = person1;
boolean result3 = person1.equals(person3);//true (same reference)
boolean result4 = person1 == person3;//true (same reference)

8. Null Comparison with ==

Object obj1 = null;
Object obj2 = null;
boolean result = (obj1 == obj2); // true

9. Null Comparison with equals()

Object obj1 = null;
Object obj2 = null;
boolean result = obj1.equals(obj2); // NullPointerException (because 'obj1' is null)

10. Comparing Objects of Different Classes

String str = "Hello";
Integer num = 5;
boolean result = (str.equals(num)); // false (different types)

JUnit 5 Test - EqualsExampleTest

Let’s create a single JUnit 5 test called EqualsExampleTest.java in the src/test/java/com/makariev/examples/core directory to demonstrate these examples.

package com.makariev.examples.core;

import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

public class EqualsExampleTest {

    @Test
    void testEquality() {
        // Example 1: Comparing Primitives with '=='
        int x = 5;
        int y = 5;
        assertThat(x == y).isTrue();

        // Example 2: Comparing Strings with '=='
        String str1 = "Hello";
        String str2 = "Hello";
        assertThat(str1 == str2).isTrue();

        // Example 3: Comparing Strings with 'equals()'
        String str3 = "Hello";
        String str4 = "Hello";
        assertThat(str3.equals(str4)).isTrue();

        // Example 4: Comparing Objects with '=='
        Object obj1 = new Object();
        Object obj2 = obj1;
        assertThat(obj1 == obj2).isTrue();

        // Example 5: Comparing Objects with 'equals()'
        String s1 = "Hello";
        String s2 = "Hello";
        assertThat(s1.equals(s2)).isTrue();

        // Example 6: Custom Class Comparison with '=='
        Person person1 = new Person("Alice");
        Person person2 = person1;
        assertThat(person1 == person2).isTrue();

        //Example 7: Custom Class Comparison with 'equals()'
        // Example 7.1: overrided 'equals()'
        Person person3 = new Person("Alice");
        Person person4 = new Person("Alice");
        assertThat(person3.equals(person4)).isTrue();

        // Example 7.2: no overrided 'equals()'
        PersonNoEquals person5 = new PersonNoEquals("Alice");
        PersonNoEquals person6 = new PersonNoEquals("Alice");
        assertThat(person5.equals(person6)).isFalse(); //different reference, no 'equals()'
        assertThat(person5 == person6).isFalse(); //different reference
        
        PersonNoEquals person7 = person5;
        assertThat(person5.equals(person7)).isTrue(); //same reference
        assertThat(person5 == person7).isTrue(); //same reference

        // Example 8: Null Comparison with '=='
        Object obj3 = null;
        Object obj4 = null;
        assertThat(obj3 == obj4).isTrue();

        // Example 9: Null Comparison with 'equals()', it will throw Exception !
        Object obj5 = null;
        Object obj6 = null;
        assertThatThrownBy(() -> {
            boolean result = obj5.equals(obj6);
            assertThat(result).isTrue(); // this line will never execute, because the previous will throw NullPointerException
        }).isInstanceOf(NullPointerException.class);

        // Example 10: Comparing Objects of Different Classes
        String str = "Hello";
        Integer num = 5;
        assertThat(str.equals(num)).isFalse();
    }

    static class Person {

        private String name;

        Person(String name) {
            this.name = name;
        }

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

    static class PersonNoEquals {

        private String name;

        PersonNoEquals(String name) {
            this.name = name;
        }

    }
}

Running the Test

To run the test, execute the following command in the project’s root directory:

mvn test

JUnit 5 and AssertJ will execute the test, and you should see output indicating whether the test passed or failed.

Conclusion

In this blog post, we’ve explored the differences between == and equals() in Java through ten practical examples. This test demonstrates the nuances of ‘==’ and equals() and serves as a valuable reference for any Java developer. Understanding when and how to use these comparison techniques is essential for writing robust and bug-free code.


Coffee Time!

Happy coding!

Share: Twitter LinkedIn