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)
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,
stris 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.