Back to Blog
Java

Java String == vs equals: Reference vs Value Comparison

java string == vs equals: Understand the difference between == and equals in Java for String comparison, including reference vs value equality and string interning.

JavaString comparisonequals methodreference equalitystring interning
Illustration of Java String comparison showing two identical-looking boxes, one linked by a chain for reference equality and the other with a checkmark for value equality.

In Java, comparing strings with == and equals produces different results because == checks reference equality while equals checks value equality. The java string == vs equals distinction is a common source of bugs, especially for developers coming from languages where == compares values. Understanding this difference is essential for writing correct string comparisons in any Java application.

How == Works on String Objects

The == operator in Java compares object references, not the content of the objects. For primitives, == compares values, but for objects, it checks whether two variables point to the exact same object in memory. This is often called reference equality.

String a = "hello"; String b = "hello"; String c = new String("hello"); System.out.println(a == b); // true, because both refer to the same interned string System.out.println(a == c); // false, because c is a new object

In this example, a and b are string literals, so the JVM places them in the string pool and reuses the same object. c is explicitly created with new, so it occupies a different memory location. Thus, a == c is false even though the content is identical.

How equals Works on String Objects

The equals method is defined in Object and overridden in the String class to compare the character sequences of two strings. It checks whether the content is exactly the same, which is known as value equality.

String a = "hello"; String c = new String("hello"); System.out.println(a.equals(c)); // true, because the content is equal

Because String overrides equals, you get a content-based comparison. This is the method you should use whenever you care about whether two strings represent the same sequence of characters.

String Interning and Its Effect on ==

The Java string pool is a special memory area where string literals are stored. When you create a string literal, the JVM checks the pool for an existing identical string and reuses it if present. This is why a == b is true for two identical literals. However, strings created with new are not automatically interned, so they reside outside the pool.

String s1 = "hello"; String s2 = "hel" + "lo"; // compile-time constant, interned String s3 = "hel" + new String("lo"); // runtime concatenation, not interned System.out.println(s1 == s2); // true System.out.println(s1 == s3); // false

Compile-time constant expressions like "hel" + "lo" are folded into a single literal and interned. Runtime concatenation involving new creates a new object. This behavior is a frequent source of confusion when developers rely on == for value comparison.

Practical Examples of == vs equals

Consider a scenario where you read a string from user input and compare it to a constant:

String input = new Scanner(System.in).nextLine(); String expected = "admin"; if (input == expected) { System.out.println("Access granted"); } else { System.out.println("Access denied"); }

This code will almost always deny access because input is a new object created from the scanner, not the interned literal. The correct check is input.equals(expected). The same problem appears when comparing strings passed as method arguments or retrieved from a collection.

Common Mistakes and Debugging

A common mistake is using == inside an overridden equals method for string fields. For example:

public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Person)) return false; Person p = (Person) o; return this.name == p.name; // wrong }

This fails because name might be a new string object. The correct implementation uses this.name.equals(p.name). Many IDEs and static analysis tools flag == on strings as a warning, but it is still easy to miss in complex code. When debugging, check both the reference identity and the content using System.identityHashCode() and equals() to understand why a comparison fails.

Performance and Runtime Considerations

The equals method performs a character-by-character comparison, so its time complexity is O(n) in the length of the string. The == operator is O(1) because it only compares references. In most applications, this difference is negligible unless you are doing millions of comparisons. However, using == for value comparison is incorrect and can lead to subtle bugs that are hard to reproduce.

There is no meaningful performance benefit to using == for strings when you need value equality, because the correctness issue outweighs any micro-optimization. If you are concerned about performance, consider using equals on a constant string first to avoid a null check, or use Objects.equals for a null-safe comparison.

When to Use == vs equals

Use == for:

  • Null checks: if (str == null)
  • Comparing enum values, because enums are singletons
  • Checking reference identity when you explicitly want to know if two variables point to the same object

Use equals for all other string comparisons where you care about content. For example, comparing user input, database values, or any string that might not be interned. The only exception is when you deliberately want to check identity, which is rare in application code.

Handling Null and Other Edge Cases

The equals method is null-safe when called on a non-null reference: "hello".equals(null) returns false. However, calling equals on a null reference throws a NullPointerException. To avoid this, you can use Objects.equals(a, b), which handles nulls gracefully, or call equals on a known constant:

String input = null; System.out.println("hello".equals(input)); // false, no exception System.out.println(input.equals("hello")); // throws NullPointerException

Also, String provides equalsIgnoreCase for case-insensitive content comparison. When implementing a custom class, always override equals and hashCode together, and use equals for string fields inside the implementation. Remember that == on strings is only reliable for interned literals, and relying on that behavior is fragile because it depends on runtime details like whether a string was created with new or through concatenation.

java string == vs equals: Practical Usage and Code Examples | RYUSLOG DEV