Skip to content

Java · Modern Java

Java Text Blocks — Multi-line Strings Without the Escape Clutter

6 min read Updated 2026-06-20 Share:

Practice Text Blocks interview questions

The problem text blocks solve

Writing multi-line strings in Java used to require either concatenation or explicit escape sequences — neither of which lets you see what the string actually looks like:

// Pre-Java-15: noisy, error-prone, hard to diff
String json = "{\n" +
              "  \"name\": \"Alice\",\n" +
              "  \"age\": 30\n" +
              "}";

String sql = "SELECT u.id, u.name\n" +
             "FROM users u\n" +
             "WHERE u.active = true";

Text blocks (finalized in Java 15, JEP 378) replace this with a multi-line literal that looks like its content:

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

String sql = """
        SELECT u.id, u.name
        FROM users u
        WHERE u.active = true
        """;

No escape sequences, no concatenation, no visual noise. The string is exactly what you see.

Syntax — opening and closing delimiters

A text block opens with """ followed by a mandatory newline — content cannot start on the same line as """. It closes with """ either on its own line or at the end of the last content line:

// Closing """ on its own line — trailing newline in result:
String s1 = """
        hello
        world
        """;
// s1 == "hello\nworld\n"

// Closing """ inline — no trailing newline:
String s2 = """
        hello
        world""";
// s2 == "hello\nworld"

A text block is a plain java.lang.String — same type, same pool, same API. The compiler evaluates it at compile time; at runtime there is no performance difference from a regular string literal.

Incidental whitespace stripping

When you indent a text block to align with its surrounding code, the compiler strips the common leading whitespace from every content line:

class Config {
    static final String QUERY = """
            SELECT *
            FROM orders
            WHERE status = 'OPEN'
            """;
}

Every content line above has 12 leading spaces; the closing """ also has 12. The compiler strips 12 spaces from every line, leaving:

SELECT *
FROM orders
WHERE status = 'OPEN'

The algorithm is: find the minimum indentation among non-blank content lines and the closing-delimiter line, then strip exactly that many spaces from each line. The closing delimiter position is therefore how you control the output indentation:

// Move closing """ left to reduce stripping (keep 4 spaces of indentation):
String indented = """
        outer
            inner
    """;
// "    outer\n        inner\n"

Line-ending normalisation

The compiler normalises all line endings to \n (LF) regardless of what the source file uses (Windows CRLF, Unix LF, old Mac CR). Text blocks are therefore safe to use in cross-platform codebases without worrying about line-ending differences:

Source file on Windows (CRLF) → compiler → text block string uses LF everywhere

If you need \r\n for a specific protocol (HTTP headers, email, CSV), add \r explicitly:

String response = """
        HTTP/1.1 200 OK\r
        Content-Type: application/json\r
        \r
        {"status":"ok"}\r
        """;

New escape sequences

Java 15 introduced two escape sequences specifically for text blocks:

Line continuation: \

Suppresses the newline at the end of a line, joining it visually wrapped text into one logical line:

String sentence = """
        The quick brown fox \
        jumps over the lazy dog.
        """;
// "The quick brown fox jumps over the lazy dog.\n"

Space anchor: \s

Preserves a trailing space that would otherwise be stripped. The compiler removes all trailing whitespace from each line; \s marks the last character as a space that must survive:

String table = """
        apple  \s
        banana \s
        cherry \s
        """;
// Each line ends with exactly one space

Value interpolation with formatted()

Text blocks don't have template syntax, but Java 15 added String.formatted() — an instance method equivalent of String.format() — which chains naturally:

String name = "Alice";
int score = 95;

String report = """
        Name:  %s
        Score: %d
        Grade: %s
        """.formatted(name, score, score >= 90 ? "A" : "B");

For simple substitution this works well. For complex templates with loops or conditionals, use a dedicated template engine (Freemarker, Thymeleaf, Mustache).

Practical examples

SQL

String query = """
        SELECT u.id, u.name, o.total
        FROM users u
        JOIN orders o ON o.user_id = u.id
        WHERE u.active = true
          AND o.created_at > :since
        ORDER BY o.total DESC
        LIMIT :limit
        """;

The query keywords align visually, making it trivial to read and diff.

JSON

String body = """
        {
          "event": "user.created",
          "payload": {
            "id": "%s",
            "email": "%s"
          }
        }
        """.formatted(userId, email);

No \" escapes around JSON field names and values.

HTML email template

String html = """
        <!DOCTYPE html>
        <html lang="en">
        <head><title>%s</title></head>
        <body>
          <h1>Hello, %s!</h1>
          <p>Your order #%s has been shipped.</p>
        </body>
        </html>
        """.formatted(subject, firstName, orderId);

Test fixture data

@Test
void parsesConfig() {
    String yaml = """
            server:
              port: 8080
              host: localhost
            database:
              url: jdbc:h2:mem:test
            """;
    Config cfg = Config.parse(yaml);
    assertThat(cfg.serverPort()).isEqualTo(8080);
}

Test fixtures defined as text blocks are readable and maintainable — no escaping, no external files for simple cases.

Trailing whitespace — the subtle gotcha

The compiler strips trailing whitespace from every line automatically. If you write:

String s = """
        hello   
        world   
        """;

Those trailing spaces after "hello" and "world" are silently removed. The result is "hello\nworld\n". If you need a trailing space to be preserved, use \s:

String s = """
        hello\s
        world\s
        """;
// "hello \nworld \n"

Runtime string utilities added with text blocks

Java 15 also added two String methods useful for text-block-like processing at runtime:

String.stripIndent() — performs the same incidental whitespace removal as the compiler, useful for strings loaded from files or databases:

String fromFile = Files.readString(path);
String cleaned = fromFile.stripIndent(); // same as compiler's text-block stripping

String.indent(int n) — adjusts indentation by n spaces per line (adds if positive, removes if negative):

String block = "line1\nline2\n";
System.out.print(block.indent(4));
//     line1
//     line2

String.translateEscapes() — interprets escape sequences in a string at runtime (useful when reading escape sequences from config files that should be treated as string content):

String raw = "hello\\nworld"; // two chars: \ and n
String escaped = raw.translateEscapes(); // "hello\nworld" — actual newline

Version history

JavaStatusDetails
13PreviewJEP 355 — first preview
14PreviewJEP 368 — second preview, added \ and \s
15StandardJEP 378 — finalized, no --enable-preview needed

Use text blocks freely in any codebase targeting Java 15+.

Recap

Text blocks are triple-quoted multi-line string literals that produce a regular String — same type, same pool, no runtime overhead. The compiler strips incidental whitespace based on the common leading prefix and the position of the closing """, and normalises all line endings to \n. Two new escape sequences cover edge cases: \ suppresses a newline (line continuation) and \s preserves a trailing space. Use .formatted() for value substitution. Text blocks are the right choice for any multi-line content — SQL, JSON, HTML, test fixtures — and stripIndent() / translateEscapes() / indent() extend the same ergonomics to runtime string handling.

More ways to practice

The self-quiz is live. Get notified when mock interviews and new question packs drop.

or
Join our WhatsApp Channel