Q4 of 40 · Core Java
What is the difference between String, StringBuilder, and StringBuffer?
Short answer
Short answer: String is immutable — every modification creates a new object. StringBuilder is mutable and fast but not thread-safe. StringBuffer is mutable and thread-safe via synchronized methods, but slower than StringBuilder. Use StringBuilder in single-threaded code, String for constants.
Detail
String objects are immutable. Every time you concatenate, trim, or replace on a String, Java creates a new String object in the heap (or string pool for literals). In a loop, s += token allocates a new String on every iteration — O(n²) time and significant GC pressure. For heavy manipulation in a single method, this is a common performance trap.
StringBuilder is a mutable character buffer. append(), insert(), delete(), and reverse() all modify the internal array in place. No synchronisation overhead. This is what the Java compiler actually emits when you write "a" + b + "c" outside a loop — it quietly rewrites that to a StringBuilder chain.
StringBuffer has the same API as StringBuilder but every method is synchronized. This makes it safe when multiple threads write to the same buffer, but that use case is rare in practice (you're more likely to use thread-local builders or a concurrent structure). In benchmarks, StringBuffer is noticeably slower than StringBuilder in single-threaded code due to lock acquisition.
For test automation, the practical rules are: use string literals and + for simple one-liners (compiler optimises it), use StringBuilder when building strings in a loop or with many conditional appends, and treat StringBuffer as a legacy class you'll encounter in older codebases but rarely write new.
// EXAMPLE
StringTypes.java
// String — immutable, each += allocates a new object
String result = "";
for (String token : tokens) {
result += token; // ❌ O(n²) allocations
}
// StringBuilder — mutable buffer, O(n) total
var sb = new StringBuilder();
for (String token : tokens) {
sb.append(token); // ✅ single buffer, grows as needed
}
String result = sb.toString();
// Compiler already uses StringBuilder for simple concatenation
String msg = "User " + userId + " failed after " + attempts + " attempts";
// ↑ compiled as: new StringBuilder().append("User ").append(userId)...
// StringBuffer — only if you genuinely need a shared mutable buffer
// across threads (rare — prefer local builders + join)
StringBuffer shared = new StringBuffer();