Complete Guide to String vs StringBuilder vs StringBuffer in Java

Learn String, StringBuilder, and StringBuffer in Java with memory concepts, mutability, thread safety, performance comparison, internal working, complex examples, and interview questions.

String vs StringBuilder vs StringBuffer in Java

String, StringBuilder, and StringBuffer are used for handling text data in Java. They look similar but differ in mutability, memory usage, thread safety, and performance.

Understanding these classes is extremely important for Java interviews, backend development, memory optimization, and performance tuning.

Main Difference

What is String in Java?

String is an immutable class in Java. Once a String object is created, its value cannot be changed.

Whenever a String is modified, Java creates a new object in memory instead of changing the original object.

String Example

String s = "Java";
s.concat(" Programming");
System.out.println(s);

Output

Java

The original string does not change because String objects are immutable.

Correct Way to Modify String

String s = "Java";
s = s.concat(" Programming");
System.out.println(s);

Output

Java Programming

Why String is Immutable

  • Provides security
  • Improves caching using String Pool
  • Makes HashMap keys reliable
  • Supports thread safety
  • Improves performance in shared environments

Real-World Importance of Immutable Strings

  • Database URLs should not change
  • File paths must remain constant
  • Network connection strings must stay secure
  • Usernames and tokens should remain unchanged

String Pool in Java

Java stores String literals inside a special memory area called the String Pool. This helps save memory by reusing identical String objects.

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

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

Output

true

Both variables point to the same memory location in the String Pool.

Memory Representation of String

String s1 = "Java";
String s2 = "Java";

String Pool
-------------
"Java"  ← s1, s2

What is StringBuilder?

StringBuilder is a mutable class introduced in Java 5. It allows modification of text without creating new objects repeatedly.

StringBuilder is faster because it does not use synchronization.

StringBuilder Example

StringBuilder sb = new StringBuilder("Java");
sb.append(" Programming");
System.out.println(sb);

Output

Java Programming

Common Methods of StringBuilder

Complex StringBuilder Example

StringBuilder report = new StringBuilder();

report.append("Employee Report\n");
report.append("-------------------\n");

for(int i = 1; i <= 5; i++) {
    report.append("Employee ID: ")
          .append(i)
          .append(" | Salary: ")
          .append(i * 5000)
          .append("\n");
}

System.out.println(report);

Output

Employee Report
-------------------
Employee ID: 1 | Salary: 5000
Employee ID: 2 | Salary: 10000
Employee ID: 3 | Salary: 15000
Employee ID: 4 | Salary: 20000
Employee ID: 5 | Salary: 25000

What is StringBuffer?

StringBuffer is also mutable like StringBuilder, but it is thread-safe because its methods are synchronized.

Synchronization makes StringBuffer slower than StringBuilder but safer in multi-threaded applications.

StringBuffer Example

StringBuffer sb = new StringBuffer("Hello");
sb.append(" World");
System.out.println(sb);

Output

Hello World

Multi-Threaded Example Using StringBuffer

class Task extends Thread {
    static StringBuffer buffer = new StringBuffer("Java");

    public void run() {
        for(int i = 0; i < 3; i++) {
            buffer.append("-").append(i);
        }
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        Task t1 = new Task();
        Task t2 = new Task();

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println(Task.buffer);
    }
}

StringBuffer prevents data corruption in concurrent environments.

Mutability Concept

Performance Comparison

Repeated String concatenation creates multiple objects and reduces performance.

Performance Problem with String

String result = "";

for(int i = 0; i < 10000; i++) {
    result += i;
}

This creates thousands of temporary String objects.

Optimized Solution Using StringBuilder

StringBuilder result = new StringBuilder();

for(int i = 0; i < 10000; i++) {
    result.append(i);
}

This approach is memory efficient and much faster.

Internal Capacity of StringBuilder

StringBuilder maintains an internal character array.

StringBuilder sb = new StringBuilder();
System.out.println(sb.capacity());

Output

16

Default capacity is 16 characters. Capacity increases automatically when needed.

Capacity Growth Formula

newCapacity = (oldCapacity * 2) + 2

StringBuilder vs StringBuffer

Real-World Use Cases

Complex Real-World Example

Generating a large JSON response in a backend API.

StringBuilder json = new StringBuilder();

json.append("{\n");
json.append("  \"employees\": [\n");

for(int i = 1; i <= 3; i++) {
    json.append("    {\n");
    json.append("      \"id\": ").append(i).append(",\n");
    json.append("      \"name\": \"Employee").append(i).append("\",\n");
    json.append("      \"salary\": ").append(i * 10000).append("\n");
    json.append("    }");

    if(i < 3) {
        json.append(",");
    }

    json.append("\n");
}

json.append("  ]\n");
json.append("}");

System.out.println(json);

Generated Output

{
  "employees": [
    {
      "id": 1,
      "name": "Employee1",
      "salary": 10000
    },
    {
      "id": 2,
      "name": "Employee2",
      "salary": 20000
    },
    {
      "id": 3,
      "name": "Employee3",
      "salary": 30000
    }
  ]
}

When to Use What

  • Use String for fixed or read-only text
  • Use StringBuilder for high-performance text modification
  • Use StringBuffer when multiple threads modify text simultaneously

Common Beginner Mistakes

  • Using String inside loops
  • Confusing mutable and immutable objects
  • Using StringBuffer in single-threaded applications unnecessarily
  • Comparing strings using == instead of equals()
  • Ignoring memory overhead caused by String concatenation

Important Interview Questions

  • Why is String immutable in Java?
  • Difference between StringBuilder and StringBuffer?
  • Which is faster and why?
  • Why are Strings stored in String Pool?
  • Why is String commonly used as HashMap key?
  • What happens when String concatenation occurs?
  • How does StringBuilder improve performance?
  • What is synchronization in StringBuffer?
  • Can StringBuilder be used in multi-threading?
  • What is the default capacity of StringBuilder?

Final Summary

  • String is immutable and memory optimized using String Pool
  • StringBuilder is mutable and fastest for modifications
  • StringBuffer is mutable and thread-safe
  • StringBuilder is preferred in most backend applications
  • StringBuffer is useful for concurrent systems
  • Choosing the correct class improves memory and performance

Conclusion

String, StringBuilder, and StringBuffer are fundamental classes in Java for handling text operations. String provides immutability and security, StringBuilder provides high performance for dynamic text generation, and StringBuffer provides thread safety for concurrent environments. Understanding their internal behavior, memory concepts, and performance impact is essential for writing efficient Java applications.