Skip to main content

3.3 String Equality & .intern() Mechanics

← Back to Chapter 3: Strings in Java

Comparing strings is one of the most common sources of logic bugs in Java applications.


== vs .equals() vs .compareTo()​

  • ==: Compares reference identity (do both pointers point to the exact same Heap memory address?).
  • .equals(Object): Compares character sequence content (checks lengths and compares bytes/chars).
  • .compareTo(String): Compares lexicographical ordering (returns negative, zero, or positive integer for sorting).
String a = "hello";
String b = new String("hello");

System.out.println(a == b); // false (Different Heap memory locations)
System.out.println(a.equals(b)); // true (Same characters: h-e-l-l-o)
Defensive String Comparison

To prevent NullPointerException, invoke .equals() from a known non-null constant:

// BAD: Throws NPE if role is null!
if (role.equals("ADMIN")) { ... }

// GOOD: Completely NPE-safe!
if ("ADMIN".equals(role)) { ... }

Compile-Time Literal Folding​

The Java compiler folds constant string literals at compile-time:

String s1 = "HelloWorld";
String s2 = "Hello" + "World"; // Computed at compile-time by javac!
System.out.println(s1 == s2); // true! Both point to the same SCP literal

String prefix = "Hello";
String s3 = prefix + "World"; // Non-constant variable expression! Evaluated at runtime
System.out.println(s1 == s3); // false!

.intern()​

Calling str.intern() queries the String Constant Pool:

  • If the pool already contains a string equal to str, the pool reference is returned.
  • If not, str is added to the pool and its reference is returned.
String heapStr = new String("Spring");
String interned = heapStr.intern();
String literal = "Spring";

System.out.println(heapStr == literal); // false
System.out.println(interned == literal); // true

intern() checks the String Pool and returns the pooled String if it exists; otherwise, it adds the String to the pool and returns the pooled reference.

Common String methods​

length()​

Returns the length of the String.

String s = "Java";
System.out.println(s.length()); //Output = 4

charAt()​

Returns the character at an index.

String s = "Java";

System.out.println(s.charAt(0)); // J
System.out.println(s.charAt(2)); // v

Index starts from 0.

substring()​

String s = "JavaDeveloper";

System.out.println(s.substring(4));
// Developer

System.out.println(s.substring(0, 4));
// Java

Remember:

substring(start, end)
↑
end is excluded

toUpperCase() / toLowerCase()​

String s = "Java";

System.out.println(s.toUpperCase()); // JAVA
System.out.println(s.toLowerCase()); // java

They return new Strings because String is immutable.

equals() vs equalsIgnoreCase()​

String a = "Java";
String b = "java";

System.out.println(a.equals(b)); // false
System.out.println(a.equalsIgnoreCase(b)); // true

compareTo()​

Used for lexicographical comparison.

String a = "Apple";
String b = "Banana";

System.out.println(a.compareTo(b));

It returns:

0 → both Strings are equal
negative → first String comes before second
positive → first String comes after second

String and final​

Don't confuse:

final String s = "Java";

with String immutability.

String is immutable regardless of whether the reference is final.

final means: The reference cannot be reassigned.

final String s = "Java";

// s = "Python"; // Compilation error

But String itself is already immutable.

final:

Reference cannot point somewhere else

String immutability:

String object's content cannot be changed

These are two different concepts.