Skip to content

Strings — String Pool, Immutability, StringBuilder, StringBuffer, and Text Blocks

DodaTech Updated 2026-06-28 7 min read

In this tutorial, you will learn about Strings. We cover key concepts, practical examples, and best practices to help you master this topic.

Java strings are immutable sequences of characters stored in a special memory area called the string pool. Unlike C where strings are mutable character arrays, Java's String class is designed for safety and performance — once created, a string's content never changes, which enables the string pool, Caching of hash codes, and secure handling of sensitive data.

What You'll Learn

  • How the string pool works and why strings are immutable
  • StringBuilder vs StringBuffer for mutable strings
  • Text blocks for multi-line strings (Java 13+)
  • Common string methods and their behavior

Why It Matters

String misuse is one of the most common performance problems in Java. Concatenating strings in a loop creates garbage objects. Using StringBuilder properly can be 100x faster. Understanding immutability also explains why String is thread-safe by default.

Real-World Use

Logging frameworks, template engines, SQL query builders, XML/JSON parsers, and web frameworks all Process strings intensively. Every HTTP request and response is string-based.


String Immutability

A String object cannot be changed after creation:

String s = "Hello";
s.toUpperCase();        // returns a NEW string "HELLO", original unchanged
System.out.println(s); // "Hello"

All methods that appear to modify a string (toUpperCase(), trim(), substring(), replace()) return new String objects.

Why Immutable?

  1. String Pooling — The JVM can safely share string literals
  2. Security — Network connections, file paths, and class names are strings; immutability prevents tampering
  3. Hash Code Caching — The hash code is computed once and cached (since the value never changes)
  4. Thread Safety — Immutable objects are inherently thread-safe

The String Pool

The string pool is a special memory region (in the heap, not the stack) where the JVM caches string literals:

String a = "Hello";
String b = "Hello";
String c = new String("Hello");

System.out.println(a == b);       // true (same reference from pool)
System.out.println(a == c);       // false (c is a new object on the heap)
System.out.println(a.equals(c));  // true (same characters)

When you write "Hello", the JVM checks the pool. If the string exists, it returns the pooled reference. If not, it creates a new string in the pool.

The new String("Hello") constructor bypasses the pool — it always creates a new object. You can manually intern a string:

String d = c.intern();
System.out.println(a == d);       // true (now from the pool)

Common String Methods

String s = "  Hello, World!  ";

s.length();                       // 17
s.charAt(0);                      // ' '
s.substring(2, 7);                // "Hello"
s.indexOf("World");               // 8
s.contains("Hello");              // true
s.startsWith("  He");             // true
s.endsWith("!  ");                // true
s.toUpperCase();                  // "  HELLO, WORLD!  "
s.toLowerCase();                  // "  hello, world!  "
s.trim();                         // "Hello, World!" (removes leading/trailing whitespace)
s.replace("World", "Java");       // "  Hello, Java!  "
s.split(", ");                    // ["  Hello", "World!  "]
s.isEmpty();                      // false
s.isBlank();                      // false (Java 11+)

Checking for Empty or Blank

String s1 = "";
String s2 = "   ";
s1.isEmpty();      // true
s2.isEmpty();      // false
s2.isBlank();      // true (Java 11+)

String Concatenation

The + Operator

String s = "Hello" + " " + "World";  // "Hello World"

The compiler optimizes simple concatenation into StringBuilder calls. However, in a loop, this optimization degrades:

// BAD: creates a new StringBuilder each iteration
String result = "";
for (int i = 0; i < 1000; i++) {
    result += i;  // equivalent to: result = new StringBuilder(result).append(i).toString()
}

StringBuilder (Not Thread-Safe, Faster)

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append(i);
}
String result = sb.toString();

StringBuilder is the recommended choice for most use cases. It is mutable and avoids creating intermediate strings.

StringBuffer (Thread-Safe, Slower)

StringBuffer buffer = new StringBuffer();
buffer.append("Hello");
buffer.append(" ");
buffer.append("World");
String result = buffer.toString();

StringBuffer is synchronized — all methods are thread-safe. Use it only when multiple threads access the same buffer. In single-threaded code, StringBuilder is faster.

Text Blocks (Java 13+, Standardized in 15)

Text blocks provide a clean way to write multi-line strings without escaping newlines and quotes:

String json = """
    {
        "name": "Alice",
        "age": 30,
        "city": "New York"
    }
    """;

Key rules:

  • Opening """ must be followed by a newline
  • The closing """ determines the indentation (leading whitespace is stripped)
  • Common indentation is stripped based on the position of the closing """
String html = """
    <html>
        <body>
            <h1>Hello, World!</h1>
        </body>
    </html>
    """;

Without text blocks, this would require explicit \n and \":

String html = "<html>\n    <body>\n        <h1>Hello, World!</h1>\n    </body>\n</html>";

Text blocks also support formatted strings with formatted():

String template = """
    Hello, %s!
    You are %d years old.
    """.formatted("Alice", 30);

String Performance Comparison

// Slow: ~100,000 ns for 10,000 iterations
String result = "";
for (int i = 0; i < 10_000; i++) {
    result += "x";
}

// Fast: ~1,000 ns for 10,000 iterations
StringBuilder sb = new StringBuilder(10_000);
for (int i = 0; i < 10_000; i++) {
    sb.append("x");
}
String result = sb.toString();

Pre-sizing the StringBuilder avoids internal array resizing.

Common Mistakes

  1. Using == instead of .equals() for string comparison. Always use .equals() for value comparison. == checks reference identity.
  2. Concatenating strings in a loop with +. Creates O(n^2) garbage. Use StringBuilder instead.
  3. Forgetting that strings are immutable. s.replace("a", "b") does not modify s — it returns a new string. Assign the result back to s.
  4. Using StringBuffer when StringBuilder suffices. StringBuffer is synchronized and slower. Use StringBuilder unless you need thread safety.
  5. Assuming isEmpty() checks for whitespace. isEmpty() only returns true for length 0. Use isBlank() (Java 11+) to check for whitespace-only strings.

Practice Questions

1. Why is String immutable in Java?
For string pooling, security, hash code caching, and thread safety.

2. What is the difference between StringBuilder and StringBuffer?
StringBuilder is not synchronized (faster, single-threaded). StringBuffer is synchronized (slower, thread-safe).

3. What does str.intern() do?
It adds the string to the string pool (if not already present) and returns the pooled reference.

4. How do text blocks handle indentation?
The closing """ determines the common leading whitespace that is stripped from each line.

5. Why is result += str in a loop bad practice?
Each iteration creates a new StringBuilder, appends, calls toString(), and assigns — resulting in O(n^2) time and excessive garbage.

Challenge Question:
Write a method String reverseWords(String sentence) that reverses the order of words but keeps each word's characters in the original order. Use StringBuilder for efficiency. Example: "Hello World Java" becomes "Java World Hello".

FAQ

What is the difference between `String` and `CharSequence`?

String implements CharSequence, which is an interface that StringBuilder, StringBuffer, and other character sequences also implement. Use CharSequence as a parameter type when you want to accept any sequence type.

{{< faq "How many objects are created by String s = new String(\"Hello\")?" "Possibly two. The literal \"Hello\" is placed in the string pool (one object). The new keyword creates a second object on the heap. Avoid this constructor unless you explicitly need a separate copy." >}}

What is the default capacity of a `StringBuilder`?

The default capacity is 16 characters. When the capacity is exceeded, the internal array is resized (typically doubled). Pre-size the StringBuilder if you know the approximate length in advance.

Can I use `switch` with strings?

Yes, Java 7+ supports String in switch statements and switch expressions. The comparison is case-sensitive.

What are the `formatted()` and `strip()` methods?

formatted() (Java 15) applies String.format() using the text block as a template. strip() (Java 11) removes leading and trailing whitespace using Unicode-aware rules, unlike trim() which only strips ASCII characters <= U+0020.

Mini Project

Write a program StringPlayground.java that:

  1. Creates a long string by concatenating numbers 1 to 1000 in a loop using + (measure time with System.nanoTime())
  2. Repeats the same with StringBuilder and compares the time
  3. Uses a text block to define a multi-line JSON string representing a person object
  4. Parses the text block manually to extract the name and age values (without using a JSON library)
  5. Demonstrates string pooling: create 5 strings with literal values, 5 with new String(), and compare them with == and .equals()
  6. Reverses a sentence word-by-word using StringBuilder

Print each section with clear labels so the output is self-documenting.

What's Next

You have mastered the fundamentals of Java syntax, data types, control flow, loops, arrays, methods, and strings. Now it is time to enter the world of object-oriented programming. Lesson 11 introduces classes and objects — constructors, the this keyword, instance vs static members, and initialization blocks.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro