☕ Java
Text Blocks
Text blocks are a Java 15 feature (previewed in Java 13 and 14) that provide multi-line string literals without manual escape sequences and concatenation. A text block begins with a triple-quote delimiter followed by a newline and ends with a triple-quote delimiter. The content between them is the string, with incidental indentation automatically stripped. Text blocks solve the readability and maintainability problems of embedding structured text — JSON, HTML, SQL, XML, YAML — in Java source code. This entry covers text block syntax, the incidental whitespace stripping algorithm, escape sequences specific to text blocks, string template integration, and the practical patterns that text blocks enable.
Text Block Syntax and the Indentation Algorithm
A text block is delimited by three double-quote characters on each end. The opening delimiter (three double quotes) must be followed immediately by a newline — no content can appear on the opening delimiter line. The closing delimiter position controls indentation stripping: the JVM finds the leftmost non-whitespace character of any content line or the column of the closing delimiter (whichever is further left) and strips that many characters of leading whitespace from every line. This is called the re-indentation algorithm.
The purpose of re-indentation is to allow the text block's content to be visually aligned with the surrounding code without the alignment appearing in the actual string value. A text block indented sixteen spaces because it appears inside a deeply nested method should not produce a string that starts with sixteen spaces — the programmer wanted the indentation for readability, not as content. The algorithm strips the "incidental" whitespace introduced purely for source code alignment.
The closing delimiter's position is significant: placing it at the end of the last content line includes no trailing newline. Placing it on its own line at the indentation level includes a trailing newline. This gives precise control over whether the produced string ends with a newline character, which matters for SQL queries, HTTP bodies, and any context where trailing newlines affect semantics.
Java
// ── Basic text block ─────────────────────────────────────────────────
String json = """
{
"name": "Alice",
"age": 30,
"active": true
}
""";
// Equivalent to:
// "{\n \"name\": \"Alice\",\n \"age\": 30,\n \"active\": true\n}\n"
// ── Indentation stripping — closing delimiter controls indent ──────────
// The closing """ is at column 8 (8 spaces from left margin)
// Java strips 8 spaces from EVERY line's leading whitespace
String sql = """
SELECT id, name, email
FROM users
WHERE active = true
ORDER BY name
""";
// Leading 8 spaces stripped — result has NO leading spaces:
// "SELECT id, name, email\nFROM users\nWHERE active = true\nORDER BY name\n"
// ── Closing delimiter position controls trailing newline ───────────────
// Closing """ on its own line → string ENDS with \n
String withNewline = """
Hello
World
"""; // ← """ on its own line: ends with \n
// Closing """ on last content line → NO trailing \n
String noNewline = """
Hello
World"""; // ← """ on content line: no trailing \n
System.out.println(withNewline.endsWith("\n")); // true
System.out.println(noNewline.endsWith("\n")); // false
// ── Alignment of closing delimiter affects content indentation ─────────
// Closing """ further left than content → extra spaces in result
String extraIndent = """
line one
line two
"""; // closing at column 8, content at column 12 → 4 leading spaces per line
// " line one\n line two\n"Text Block Escape Sequences
Text blocks support all standard Java escape sequences (\n, \t, \\, \", etc.) plus two new escape sequences introduced specifically for text blocks. The line terminator escape \<newline> suppresses the actual newline at the end of a source line, allowing long logical lines to be physically split across multiple source lines without the newline appearing in the string value. This is useful for long SQL statements or long prose where breaking the visual line for readability should not produce a multi-line string.
The space escape \s inserts a single space character that is significant to the re-indentation algorithm. Trailing whitespace on text block lines is normally stripped — the compiler removes spaces and tabs that appear at the end of content lines. This stripping is usually the right behaviour (trailing whitespace in source is almost never intentional content), but occasionally trailing spaces are meaningful (in test expectations, in protocol messages). The \s escape preserves the space at a specific position by replacing it with a non-stripped marker that produces a space in the final string.
Normal double quotes do not need escaping inside text blocks, which is one of their main readability advantages for JSON, HTML, and SQL content. Triple double quotes within the content must be broken up, typically by escaping one quote: \""" or by inserting a \s to break the triple.
Java
// ── Standard escapes work inside text blocks ─────────────────────────
String withTab = """
Column1\tColumn2\tColumn3
value1\tvalue2\tvalue3
""";
// ── \<newline> — suppress line break in the source ────────────────────
// This SQL appears on two source lines but is one logical line:
String sql = """
SELECT id, name, email FROM users \
WHERE active = true AND age > 18 \
ORDER BY name LIMIT 100
""";
// Result: "SELECT id, name, email FROM users WHERE active = true AND age > 18 ORDER BY name LIMIT 100\n"
// (The \ at end of each line suppresses the newline)
// ── \s — preserve trailing space ──────────────────────────────────────
// Without \s, trailing spaces are stripped:
String noTrail = """
line one
line two
""";
// "line one\nline two\n" — trailing spaces removed
// With \s, space before \s is preserved:
String withTrail = """
line one \s
line two \s
""";
// "line one \nline two \n" — five trailing spaces preserved on each line
// ── Double quotes do NOT need escaping inside text blocks ─────────────
String html = """
<a href="https://example.com" class="link">
Click "here" to continue
</a>
""";
// No escaping needed for " — major readability improvement over regular strings
// ── Triple quote inside text block — must break the triple ───────────
// Option 1: escape one quote
String code = """
String msg = \"""Hello\""";
""";
// Option 2: use \s to break the triple
String code2 = """
String msg = """\s""";
""";
// ── Before vs after text blocks — JSON comparison ─────────────────────
// Before (Java 14 and earlier):
String jsonOld = "{\n" +
" \"name\": \"Alice\",\n" +
" \"age\": 30,\n" +
" \"active\": true\n" +
"}";
// After (Java 15+):
String jsonNew = """
{
"name": "Alice",
"age": 30,
"active": true
}
""";Practical Patterns — SQL, HTML, JSON
Text blocks transform the ergonomics of embedding structured text in Java code. SQL queries become readable multi-line statements that mirror how a database developer would write them. HTML templates look like HTML. JSON payloads look like JSON. This readability improvement is not cosmetic — it reduces errors caused by misreading or mistyping escaped string content, and it makes the code self-documenting.
The formatted() method (instance method on String since Java 15) works naturally with text blocks to produce parameterised text. Calling textBlock.formatted(arg1, arg2) applies printf-style substitution and returns a new String with the arguments substituted. This is cleaner than String.format(textBlock, args) but semantically identical.
Text blocks are interned like regular string literals — they participate in the string pool. A text block that appears multiple times in code with the same content resolves to the same pool entry. Text blocks are also String objects in every other respect: they can be assigned to String variables, passed to String parameters, and used wherever String is expected.
Java
// ── SQL with text block ──────────────────────────────────────────────
String findActiveUsers = """
SELECT
u.id,
u.name,
u.email,
COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE u.active = true
AND u.created_at > :cutoffDate
GROUP BY u.id, u.name, u.email
HAVING COUNT(o.id) > :minOrders
ORDER BY order_count DESC
""";
// ── HTML template ─────────────────────────────────────────────────────
String emailTemplate = """
<!DOCTYPE html>
<html>
<body>
<h1>Welcome, %s!</h1>
<p>Your account has been created.</p>
<p>Click <a href="%s">here</a> to verify.</p>
</body>
</html>
""";
// ── formatted() — substitute values into text block ───────────────────
String email = emailTemplate.formatted("Alice", "https://example.com/verify/abc123");
// ── JSON payload ──────────────────────────────────────────────────────
String createUserBody = """
{
"username": "%s",
"email": "%s",
"role": "USER",
"active": true
}
""".formatted(username, email);
// ── YAML configuration ────────────────────────────────────────────────
String yamlConfig = """
server:
port: %d
host: %s
database:
url: %s
pool-size: %d
""".formatted(port, host, dbUrl, poolSize);
// ── Test assertions — expected multi-line output ───────────────────────
@Test
void formatReport_producesExpectedLayout() {
String expected = """
ID Name Score
-- ---- -----
1 Alice 95
2 Bob 87
""";
String actual = formatter.formatReport(testData);
assertThat(actual).isEqualTo(expected);
}
// ── stripIndent() and translateEscapes() — programmatic manipulation ──
// stripIndent() applies the text block re-indentation algorithm to a runtime string
String indented = " line1\n line2\n line3\n";
String stripped = indented.stripIndent();
System.out.println(stripped); // "line1\nline2\nline3\n"
// translateEscapes() interprets \n, \t etc. in a runtime string
String withEscapes = "line1\\nline2\\nline3";
System.out.println(withEscapes.translateEscapes()); // prints on three linesRelated Topics in Strings
String Class
String is one of the most fundamental classes in Java — used in virtually every program, yet deeply misunderstood by many developers. A String represents an immutable sequence of Unicode characters. It is not a primitive type but a full class in java.lang, automatically imported into every Java file. Understanding String means understanding how it is stored in memory, why it is immutable, how the string pool works, what the difference between == and equals() means for strings, and how to use the class efficiently. This entry covers String's nature as a class, its internal representation, the critical distinction between reference equality and value equality, String's place in the type hierarchy, and the design decisions that make String behave the way it does.
String Pool
The string pool (also called the string intern pool or string constant pool) is a special memory region maintained by the JVM that stores a single copy of each unique string value. When two string literals have the same content, they refer to the same object in the pool rather than two separate objects. The pool is a flyweight pattern applied at the language level — it dramatically reduces memory consumption in applications that use many repeated string values, which is nearly every application. This entry covers how the pool works, where it lives in JVM memory, how to interact with it programmatically, the intern() method, performance implications, and when to use or avoid pool entries.
Immutable String
String immutability is the most important design decision in Java's String class. Once a String object is created, its character sequence can never change. No method on String modifies the string; every method that appears to modify returns a new String object containing the result. This design decision drives thread safety, enables the string pool, makes strings safe hash map keys, and simplifies reasoning about string values. Understanding why String is immutable, how immutability is enforced, and what the consequences of immutability are clarifies the behaviour of virtually every piece of Java code that handles strings.
Mutable String
Java provides two mutable string classes for scenarios where String's immutability would be inefficient: StringBuilder and StringBuffer. Both maintain an internal character buffer that can be modified in place — characters can be appended, inserted, deleted, and replaced without creating new objects. StringBuilder is the modern choice for single-threaded use; StringBuffer is the legacy thread-safe version with synchronised methods. This entry covers the internal buffer mechanics, the full API of both classes, performance characteristics, when to use each, thread safety implications, and the complete patterns for efficient string construction.