1.3 Strings, Text Blocks, and StringBuilder
Key Takeaways
- String objects are immutable and managed via the String Constant Pool, where compile-time constant expressions are interned automatically while new String() allocations bypass the pool.
- Java SE 21 provides rich String inspection and transformation methods including strip(), stripIndent(), translateEscapes(), formatted(), lines(), isBlank(), and transform().
- Text Blocks (triple double-quotes) standardize multi-line strings by automatically removing incidental whitespace according to the leftmost non-whitespace character margin.
- StringBuilder provides mutable in-place character sequence manipulation, but does not override equals(), meaning identity equality is performed unless comparing string representations.
Strings, Text Blocks, and StringBuilder
String processing is central to modern Java development and represents a substantial portion of the 1Z0-830 exam. This section covers String immutability, the String Constant Pool, modern Java 11 through Java 21 String utility methods, Text Blocks (JEP 378), and mutable StringBuilder mechanics.
1. String Immutability and the String Constant Pool
In Java, java.lang.String objects are immutable: once constructed, their character sequence cannot be modified. Any method that appears to modify a String (concat(), toUpperCase(), replace()) returns a brand-new String instance.
The String Constant Pool
To optimize memory, the JVM maintains a dedicated String Constant Pool in heap memory:
- String Literals: Literal strings (e.g.,
"Java") and compile-time constant expressions (e.g.,"Ja" + "va") are placed into the pool at class-load time. Identical string literals share the exact same object reference. new String(...)Allocations: Explicitly invokingnew String("Java")creates a new distinct object on the regular heap outside the constant pool.String.intern(): Calling.intern()on any string returns the canonical reference from the constant pool (adding it if not already present).
String s1 = "Java";
String s2 = "Java";
String s3 = new String("Java");
String s4 = s3.intern();
System.out.println(s1 == s2); // true (Both refer to same pooled literal)
System.out.println(s1 == s3); // false (s3 is a separate heap instance)
System.out.println(s1 == s4); // true (s4 points to pooled instance)
System.out.println(s1.equals(s3)); // true (Content comparison)
Compile-Time vs. Runtime Concatenation
- Compile-time Constants: Concatenations involving only literals or
finalvariables initialized with constant expressions are resolved by the compiler into a single pooled literal. - Runtime Expressions: Concatenations involving non-final variables or method calls are resolved at runtime, creating a new heap object that is not pooled.
String base = "Java";
final String finalBase = "Java";
String str1 = "Java21";
String str2 = "Java" + 21; // Resolved at compile time -> "Java21" (pooled)
String str3 = base + 21; // Resolved at runtime -> New heap object
String str4 = finalBase + 21; // Resolved at compile time -> "Java21" (pooled)
System.out.println(str1 == str2); // true
System.out.println(str1 == str3); // false
System.out.println(str1 == str4); // true
2. Essential Modern String Methods (Java 11 to 21)
Java provides modern built-in methods for inspection, transformation, and whitespace handling:
| Method | Return Type | Description |
|---|---|---|
isEmpty() | boolean | Returns true if length() == 0. |
isBlank() | boolean | Returns true if empty or containing only Unicode whitespace codepoints. |
strip() | String | Removes leading and trailing Unicode whitespace (unlike trim(), which only handles ASCII \u0020 and lower). |
stripLeading() | String | Removes leading Unicode whitespace. |
stripTrailing() | String | Removes trailing Unicode whitespace. |
repeat(int count) | String | Returns string concatenated with itself count times ("ab".repeat(3) $\rightarrow$ "ababab"). |
lines() | Stream<String> | Returns a stream of lines separated by \n, \r, or \r\n. |
indent(int n) | String | Adjusts indentation of each line by n spaces and ensures a trailing newline. |
stripIndent() | String | Removes the common leading indentation from every line and strips trailing whitespace from each line. |
translateEscapes() | String | Translates escape sequences (\n, \t, etc.) into literal characters. |
formatted(Object... args) | String | Equivalent to String.format(this, args). |
transform(Function<String, R> f) | R | Applies a function to this string and returns the result (fluent chaining). |
String test = " Hello Java 21! \n Line 2 ";
// Whitespace and lines
System.out.println(" ".isBlank()); // true
System.out.println(" ".isEmpty()); // false
System.out.println(test.strip()); // "Hello Java 21!\n Line 2"
// Stream of lines
test.lines().forEach(line -> System.out.println("Line: " + line.strip()));
// Functional transform
Integer length = "Java 21".transform(String::strip).transform(String::length);
System.out.println("Length: " + length); // 7
// Fluent formatted
String msg = "User %s logged in with role %s".formatted("Duke", "ADMIN");
3. Text Blocks (JEP 378)
Text blocks provide multi-line string literals without the need for escape sequences like \n.
Syntax and Opening Delimiter Rule
A text block begins with three double-quotes """ followed by an immediate line terminator. Any characters (except whitespace comments) on the same line as the opening """ result in a compile-time error.
// Valid text block
String html = """
<html>
<body>Hello</body>
</html>
""";
// COMPILE ERROR: Character on same line as opening delimiter
// String bad = """<html>
// <body></body>
// </html>""";
Incidental Whitespace Calculation
The compiler determines incidental whitespace by finding the leftmost non-whitespace character among all lines in the text block (including the closing """ line if placed on its own line). That common prefix of spaces is stripped from every line.
// Closing delimiter controls indentation margin
String block1 = """
Line 1
Line 2
"""; // Closing delimiter indented by 4 spaces -> Result has 0 leading spaces
String block2 = """
Line 1
Line 2
"""; // Closing delimiter at column 0 -> Result preserves 4 leading spaces
Newline Control and Text Block Escape Sequences
- Line Continuation (
\): Placing a backslash\at the very end of a line suppresses the newline, joining it with the following line. - Explicit Trailing Space (
\s): Prevents the compiler from stripping trailing spaces at the end of a line.
String sql = """
SELECT id, name, email \
FROM users \
WHERE active = true
""";
// Result is a single line: "SELECT id, name, email FROM users WHERE active = true\n"
String formatted = """
Line 1 \s
Line 2
"""; // Line 1 preserves 3 trailing spaces before newline
4. StringBuilder Class Mechanics
StringBuilder represents a mutable sequence of characters. It is not synchronized (unlike legacy StringBuffer), providing superior performance in single-threaded environments.
Capacity vs. Length
length(): The count of actual characters currently stored.capacity(): The total allocated buffer size. Default initial capacity is16characters. When the buffer overflows, it expands automatically: $\text{newCapacity} = (\text{oldCapacity} \times 2) + 2$.
Critical StringBuilder Mutating Methods
StringBuilder sb = new StringBuilder("Java"); // length = 4, capacity = 20 (16 + 4)
// 1. append(): Appends to the end and returns this
sb.append(" 21"); // "Java 21"
// 2. insert(int offset, ...): Inserts content starting at index
sb.insert(4, " SE"); // "Java SE 21"
// 3. delete(int start, int end): Removes characters from start (inclusive) to end (exclusive)
sb.delete(4, 7); // "Java 21"
// 4. deleteCharAt(int index): Removes single character at index
sb.deleteCharAt(4); // "Java21"
// 5. replace(int start, int end, String str): Replaces range [start, end) with str
sb.replace(4, 6, " 2026"); // "Java 2026"
// 6. reverse(): Reverses the sequence in-place
sb.reverse(); // "6202 avaJ"
// 7. substring(int start, int end): Returns a NEW String! Does NOT mutate StringBuilder!
String sub = sb.substring(0, 4); // sub = "6202", sb remains "6202 avaJ"
The StringBuilder.equals() Trap
StringBuilder does not override equals() or hashCode() from java.lang.Object. Calling sb1.equals(sb2) performs reference identity comparison (sb1 == sb2), returning false even if their textual contents are identical!
StringBuilder sbA = new StringBuilder("Certified");
StringBuilder sbB = new StringBuilder("Certified");
System.out.println(sbA == sbB); // false (Different objects)
System.out.println(sbA.equals(sbB)); // false (Object.equals reference check!)
System.out.println(sbA.compareTo(sbB) == 0); // true (Lexicographical content check)
System.out.println(sbA.toString().equals(sbB.toString())); // true (String content check)
What is the output of the following Java code snippet? String s1 = "Oracle"; String s2 = "Ora"; String s3 = s2 + "cle"; String s4 = "Ora" + "cle"; final String s5 = "Ora"; String s6 = s5 + "cle"; System.out.println((s1 == s3) + " " + (s1 == s4) + " " + (s1 == s6));
Consider the following text block declaration in Java SE 21:
String query = """
SELECT name,
salary
FROM employees
""";
System.out.print(query);
What will the following program output? public class StringBuilderTest { public static void main(String[] args) { StringBuilder sb = new StringBuilder("1Z0-830"); sb.substring(0, 3); sb.append("-PASS"); sb.insert(3, ":EXAM"); sb.delete(12, 17); System.out.println(sb); } }
Which of the following method calls on the String instance s = " Duke " will produce the integer 4?