Text Blocks
Text blocks, finalized in Java 15 (after preview in Java 13–14) and delimited by triple-quotes ("""), let a multi-line string literal be written exactly as it should appear, without escaping embedded double quotes, manually inserting \n at every line break, or breaking the string into multiple concatenated lines — directly addressing the readability problems of embedding multi-line content like JSON, SQL, HTML, or any block-structured text as a traditional single-line string literal. This entry covers exactly what made traditional string literals awkward for multi-line content, text block syntax and the incidental-whitespace stripping algorithm that determines indentation, escape sequences and the new \ and \s escapes specific to text blocks, and interaction with String.format and string interpolation.
Motivation — Multi-Line Content in Single-Line String Literals
// ── Before Java 15 — multi-line JSON squeezed into escaped concatenation
String jsonOld = "{\n" +
" \"name\": \"Alice\",\n" +
" \"age\": 30,\n" +
" \"city\": \"Wonderland\"\n" +
"}";
// Source code bears little visual resemblance to the actual JSON it represents —
// reader must mentally undo the escaping and concatenation to see the real content
// ── Java 15+ — text block, content written exactly as it should appear ─
String json = """
{
"name": "Alice",
"age": 30,
"city": "Wonderland"
}
""";
// Embedded double quotes need NO escaping at all (only the triple-quote delimiter
// itself is special) — and the source IS the content, line breaks included
// ── SQL example — same dramatic readability improvement ────────────────
String sqlOld = "SELECT id, name, email\n" +
"FROM users\n" +
"WHERE active = true\n" +
"ORDER BY name";
String sql = """
SELECT id, name, email
FROM users
WHERE active = true
ORDER BY name
""";Incidental Whitespace Stripping — How Indentation Is Determined
// ── Closing delimiter position determines the stripped margin ──────────
String text1 = """
Hello
World
""";
// Closing """ aligned with "Hello"/"World" — that indentation level is the margin,
// stripped uniformly: text1 == "Hello\nWorld\n"
String text2 = """
Hello
World
""";
// Closing """ flush left — LESS indentation is treated as the margin, so MORE
// leading whitespace is preserved on the content lines than probably intended:
// text2 == " Hello\n World\n" (extra leading spaces kept!)
// ── Essential (intentional) indentation within content IS preserved ────
String html = """
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
""";
// The relative indentation of <li> beneath <ul> IS essential and IS preserved —
// only the common margin shared by ALL lines (matching the closing delimiter) is stripped
// ── Trailing whitespace is stripped automatically — use \s to preserve it
String trimmedTrailing = """
line with trailing spaces
next line
""";
// Trailing spaces after "spaces" on the first line are stripped automatically,
// regardless of how many were typed — avoids invisible-whitespace diff noise
String preserved = """
line ending with one preserved space\s
next line
""";
// "\s" specifically preserves exactly one trailing space that would otherwise be strippedEscape Sequences, the \ Line-Continuation Escape, and Combining with format()
// ── Ordinary escapes still work; \" rarely needed (only 3+ quotes are ambiguous)
String quotes = """
She said "hello" to "everyone" in the room.
""";
// Single/double quotes need no escaping at all — only an actual run of 3
// consecutive quotes inside the block would require escaping at least one:
String trickyQuotes = """
The delimiter looks like \"""this if it needs escaping.
""";
// ── The new "\" line-continuation escape — suppresses a line break ────
String longLine = """
This is a very long logical line that we want to \
wrap across multiple source lines purely for editor \
readability, without those wraps becoming real newlines.
""";
// Resulting string has NO embedded newlines from the wrapped portion above —
// it reads as one continuous line despite being written across three
// ── Combining text blocks with formatted()/String.format() ─────────────
String template = """
Dear %s,
Your order #%d has shipped and will arrive by %s.
Thank you for your business.
""";
String message = template.formatted("Alice", 12345, "Friday");
// formatted() is equivalent to String.format(template, args) — added in Java 15
// specifically so this combination reads naturally without a separate static call
String sqlTemplate = """
SELECT * FROM orders
WHERE customer_id = %d
AND status = '%s'
""";
String query = sqlTemplate.formatted(42, "SHIPPED");