STRING VS
STRINGBUILDER VS
STRINGBUFFER
I still remember one night debugging a slow API in my Spring Boot project. Everything looked clean, proper caching, neat service layers, even decent SQL queries. But something felt off. The CPU spikes were weird, and memory usage kept creeping up.
String response = "";
for (int i = 0; i < 10_000; i++) {
response += i;
}
One tiny innocent-looking String concatenation inside a loop.
That’s when I finally understood: String, StringBuilder, and StringBuffer aren’t the same, and using the wrong one silently kills performance.
Let me share the lesson I wish someone had told me earlier.
The Truth About String: Immutable but Dangerous (Sometimes)
String in Java is immutable. Once created, it can’t change. Every time you “modify” a String, Java actually:
- check_circle Creates a new object
- check_circle Throws away the old one
Great for:
- check_circle Constants
- check_circle HTTP header values
- check_circle Configuration keys
- check_circle Simple text processing
A disaster for:
- check_circle Loops that build dynamic text
- check_circle Large JSON/XML/HTML generation
- check_circle Repeated concatenation
In a Spring Boot app, I used String everywhere: logging, building responses, dynamic SQL, until I realized I was creating thousands of unnecessary objects.
Sample (Slow)
String result = "";
for (int i = 0; i < 10000; i++) {
result += i; // Creates new object every loop
}This looks harmless but causes heavy GC pressure.
StringBuilder: The Partner You Actually Need
After that painful debugging session, I switched to StringBuilder, and suddenly everything felt lighter.
StringBuilder is:
- check_circle Mutable
- check_circle Fast
- check_circle Non-thread-safe (and that’s usually perfectly fine in Spring Boot)
Most Spring Boot code runs in single-threaded request contexts, so there’s no shared mutation risk. That makes StringBuilder the best default for constructing dynamic text.
Sample (Fast)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.append(i);
}
String result = sb.toString();No object churn. No GC storms. Just clean performance.
StringBuffer: The Old Guard You Rarely Need
Then there’s StringBuffer. It behaves exactly like StringBuilder but adds synchronization, meaning it’s thread-safe.
Here’s the catch:
- check_circle In modern Spring Boot apps, you almost never manually share a text builder across multiple threads.
- check_circle If you do, the design is usually questionable.
StringBuffer is a relic from early Java days think pre-Spring era. But yes, if you absolutely need safe mutation across threads, it’s there.
StringBuffer sb = new StringBuffer();
sb.append("Sawit adalah");
sb.append(" pohon juga");Slowest among the three, but safe.
How Spring Boot Fits Into This Story
When building Spring Boot applications, here’s the practical rule I finally settled on:
Use String for simple, fixed messages
Example
String appName = "PaymentService";Use StringBuilder inside services or utilities for building content
Example
public String buildTraceLog(RequestData data) {
StringBuilder sb = new StringBuilder();
sb.append("RequestId: ").append(data.getId());
sb.append(", User: ").append(data.getUser());
sb.append(", Time: ").append(data.getTime());
return sb.toString();
}Use StringBuffer only when working with explicitly multi-threaded tasks
Running tasks across ExecutorService:
StringBuffer buffer = new StringBuffer();
Runnable r = () -> buffer.append(Thread.currentThread().getName()).append(" ");
executor.submit(r);Rarely used in actual Spring Boot business logic.
The Day After I Switched
After refactoring that API endpoint to use StringBuilder, the improvement was immediate:
- check_circle CPU dropped
- check_circle Latency improved by almost 30%
- check_circle GC logs became clean
- check_circle The on-call team stopped tagging me at 2 AM
That tiny detail choosing the right type, taught me that performance issues are often death by a thousand cuts. And most of those cuts come from using the wrong tool for the job.
Final Advice
If you’re building Spring Boot apps:
- check_circle Reach for String when you want safety and clarity
- check_circle Reach for StringBuilder when building dynamic text
- check_circle Forget StringBuffer exists unless you're intentionally dealing with threads
Most developers I mentor struggle with this not because it’s hard but because nobody explains why it matters.
Performance problems rarely shout. They whisper and then explode during peak traffic.
Make the switch early. Your future self (and your production logs) will thank you.