Next-Gen App & Browser Testing Cloud
Trusted by 2 Mn+ QAs & Devs to accelerate their release cycles

To compare two strings in Java, use equals() for an exact content match, equalsIgnoreCase() to ignore case, compareTo() or compareToIgnoreCase() for lexicographic ordering, and Objects.equals() when either value might be null. Avoid the == operator for content checks: it compares object references, not characters, so two strings with identical text can still evaluate to false. The example program below demonstrates every method with its output.
A String in Java is an immutable sequence of characters, so a comparison never changes either operand; it only reports how the two values relate. These are the standard ways to compare them:
boolean. It is case-sensitive, so "apple".equals("Apple") is false. This is the default choice for checking whether two strings hold the same text.equals() but case-insensitive, so "apple".equalsIgnoreCase("APPLE") returns true. Use it for user input, file names, or tokens where case should not matter.int rather than a boolean: 0 when equal, a negative value when the calling string sorts first, and a positive value when it sorts later. The magnitude equals the Unicode difference of the first mismatched character.compareTo() but folds case before comparing, so "apple".compareToIgnoreCase("APPLE") returns 0. Useful for case-insensitive sorting.java.util.Objects. It returns true for two nulls, false if only one is null, and otherwise delegates to equals(), so it never throws a NullPointerException.String against any CharSequence, including a StringBuilder or StringBuffer. It is the right call when the other value is not itself a String.true only when both variables point to the exact same object in memory, which makes it unsuitable for content comparison (explained below).The table below summarizes what each approach compares and what it returns, so you can pick the right one before writing a single line of code.
| Method | Compares | Returns |
|---|---|---|
equals() | Content, case-sensitive | boolean |
equalsIgnoreCase() | Content, case-insensitive | boolean |
compareTo() | Lexicographic order, case-sensitive | int (0, <0, >0) |
compareToIgnoreCase() | Lexicographic order, case-folded | int (0, <0, >0) |
Objects.equals() | Content, null-safe | boolean |
contentEquals() | Content vs any CharSequence | boolean |
== | Reference (same object), not content | boolean |
The following program is fully compilable and demonstrates each method on the same inputs so you can see exactly how their results differ.
import java.util.Objects;
public class CompareStrings {
public static void main(String[] args) {
String s1 = "apple";
String s2 = "apple"; // same literal, pooled
String s3 = new String("apple"); // new object, same content
// 1. equals() - content, case-sensitive
System.out.println("equals (apple, apple): " + s1.equals(s2));
System.out.println("equals (apple, Apple): " + s1.equals("Apple"));
// 2. equalsIgnoreCase() - content, ignores case
System.out.println("equalsIgnoreCase (apple, APPLE): " + s1.equalsIgnoreCase("APPLE"));
// 3. compareTo() - lexicographic, returns int
System.out.println("compareTo (apple, apple): " + s1.compareTo("apple"));
System.out.println("compareTo (apple, Apple): " + s1.compareTo("Apple"));
System.out.println("compareTo (apple, banana): " + s1.compareTo("banana"));
// 4. compareToIgnoreCase() - lexicographic, case-folded
System.out.println("compareToIgnoreCase (apple, APPLE): " + s1.compareToIgnoreCase("APPLE"));
// 5. == operator - reference, NOT content
System.out.println("== (s1, s2 literals): " + (s1 == s2));
System.out.println("== (s1, s3 new String): " + (s1 == s3));
System.out.println("equals (s1, s3): " + s1.equals(s3));
// 6. Objects.equals() - null-safe content check
System.out.println("Objects.equals (apple, null): " + Objects.equals(s1, null));
System.out.println("Objects.equals (null, null): " + Objects.equals(null, null));
// 7. contentEquals() - String vs CharSequence
System.out.println("contentEquals (apple, StringBuilder apple): "
+ s1.contentEquals(new StringBuilder("apple")));
}
}Output:
equals (apple, apple): true
equals (apple, Apple): false
equalsIgnoreCase (apple, APPLE): true
compareTo (apple, apple): 0
compareTo (apple, Apple): 32
compareTo (apple, banana): -1
compareToIgnoreCase (apple, APPLE): 0
== (s1, s2 literals): true
== (s1, s3 new String): false
equals (s1, s3): true
Objects.equals (apple, null): false
Objects.equals (null, null): true
contentEquals (apple, StringBuilder apple): trueNotice that compareTo("apple", "Apple") returns 32, the Unicode gap between lowercase 'a' (97) and uppercase 'A' (65), while compareTo("apple", "banana") returns -1 because 'a' (97) precedes 'b' (98). The two == lines are the most instructive: identical literals share one pooled object and report true, but s3 built with new String() is a separate object and reports false, even though equals() confirms the content is the same.
The == operator answers a different question than most developers expect. It checks whether two variables refer to the same object, not whether they hold the same characters. With strings, the result depends on how each value was created:
"apple" and "apple" are deduplicated into one object, so == happens to return true. This is a memory optimization, not a content guarantee.new String("apple") forces a fresh object on the heap. Now == returns false against the pooled literal even though both contain "apple". Values read at runtime, for example from input, files, or network responses, are typically not pooled either.s3.intern() returns the canonical pooled reference, so s1 == s3.intern() is true again. This confirms == is purely about object identity.== "works" with literals during quick tests, the bug often hides until production data flows in. Always use equals() (or Objects.equals() for null safety) to compare string content.equals(). It is the everyday choice for "are these two strings the same text?"equalsIgnoreCase() for emails, usernames, command keywords, and other case-agnostic input.compareTo() (or compareToIgnoreCase()), since collections, comparators, and binary searches rely on its int ordering result.Objects.equals() to sidestep NullPointerException without manual null checks.contentEquals() when the other operand is a StringBuilder, StringBuffer, or any CharSequence.== for the rare case where you genuinely need to know two variables point to the same object, never for comparing text.String comparison logic also shows up constantly in automated UI tests, where assertions verify that on-screen text matches expected values. When you run such Java tests across browsers and operating systems on the TestMu AI Selenium Automation cloud, the same equals() and equalsIgnoreCase() patterns drive your assertions, so getting them right locally keeps your suite reliable at scale.
The == operator compares references, meaning it checks whether two variables point to the same object in memory. The .equals() method compares the actual character content. Two strings with identical text can return false with == if they are separate objects, but .equals() always returns true when the content matches. For content comparison, always use .equals().
Use equalsIgnoreCase() for a true or false content check that ignores upper and lower case, for example "Apple".equalsIgnoreCase("apple") returns true. If you need ordering instead of equality, use compareToIgnoreCase(), which returns an int and treats the two strings as equal in case-folded form.
compareTo() returns an int based on lexicographic (dictionary) order. It returns 0 when the strings are equal, a negative number when the calling string comes before the argument, and a positive number when it comes after. The magnitude is the Unicode difference of the first non-matching character.
String literals are stored in the string constant pool, so two identical literals reference the same object and == happens to return true. As soon as one string is created with new String(), it becomes a separate object and == returns false even though the content matches. This coincidence is exactly why == is unsafe for content checks.
Use Objects.equals(a, b) from java.util.Objects. It returns true when both are null, false when only one is null, and otherwise delegates to equals(). This avoids the NullPointerException you would get from calling a.equals(b) when a is null.
equals() only returns true when the argument is also a String with matching content. contentEquals() compares a String against any CharSequence, such as a StringBuilder or StringBuffer, so "apple".contentEquals(new StringBuilder("apple")) returns true, whereas equals() on the same StringBuilder would return false.
KaneAI - Testing Assistant
World’s first AI-Native E2E testing agent.

TestMu AI forEnterprise
Get access to solutions built on Enterprise
grade security, privacy, & compliance