Master Java String internals, the String pool, StringBuilder, and common manipulation methods.
Published February 1, 2025
Strings are one of the most-used types in Java — and one of the most commonly misused. Understanding String internals prevents subtle bugs and performance issues.
Java Strings are immutable — every modification creates a new String object.
String s = "hello";
s = s.toUpperCase(); // creates a NEW String "HELLO"; original unchanged
Immutability makes Strings thread-safe and suitable as HashMap keys, but naive concatenation in loops is expensive.
String a = "hello"; // stored in String pool
String b = "hello"; // same reference from pool
String c = new String("hello"); // new heap object (avoid this)
System.out.println(a == b); // true (same pool reference)
System.out.println(a == c); // false (different objects)
System.out.println(a.equals(c)); // true (same content)
Always use .equals() to compare String content, never ==.
// Bad: creates N intermediate String objects
String result = "";
for (String s : list) {
result += s; // O(n²) time!
}
// Good: single mutable buffer
StringBuilder sb = new StringBuilder();
for (String s : list) {
sb.append(s);
}
String result = sb.toString();
// Common builder operations
sb.append("text");
sb.insert(0, "prefix");
sb.delete(2, 5);
sb.reverse();
sb.replace(1, 3, "new");
String s = " Hello, World! ";
// Inspection
s.length() // 17
s.isEmpty() // false
s.isBlank() // false (Java 11+)
s.charAt(2) // 'H'
s.indexOf('o') // 4
s.contains("World") // true
// Transformation
s.trim() // "Hello, World!"
s.strip() // Java 11+, handles Unicode whitespace
s.toLowerCase() // " hello, world! "
s.toUpperCase() // " HELLO, WORLD! "
s.replace('l', 'r') // " Herro, Worrd! "
s.replaceAll("[aeiou]", "*") // regex replace
// Splitting and joining
s.split(",") // [" Hello", " World! "]
String.join(", ", "a", "b", "c") // "a, b, c"
String.join("-", List.of("x", "y")) // "x-y"
// Substring
s.substring(2, 7) // "Hello"
s.startsWith("Hello", 2) // true
s.endsWith("!" ) // true (after trim)
// Java 11+ methods
" ".isBlank() // true
"a\nb\nc".lines().count() // 3
"ab".repeat(3) // "ababab"
" hi ".stripLeading() // "hi "
" hi ".stripTrailing() // " hi"
// String.format
String msg = String.format("Hello, %s! You are %d years old.", name, age);
// Java 15+ Text Blocks
String json = """
{
"name": "%s",
"age": %d
}
""".formatted(name, age);
// String to char array and back
char[] chars = s.toCharArray();
String back = new String(chars);
// Reverse a string
String reversed = new StringBuilder(s).reverse().toString();
// Check palindrome
public boolean isPalindrome(String s) {
int left = 0, right = s.length() - 1;
while (left < right) {
if (s.charAt(left++) != s.charAt(right--)) return false;
}
return true;
}
== — this is a classic trap. Always .equals() or Objects.equals().String.valueOf(null) returns "null" (the string), but null.toString() throws NPE.StringBuilder is single-threaded; StringBuffer is synchronized (thread-safe but slower) — rarely needed today.