Java Module 4 – Arrays and strings in depth
Text blocks for multi-line strings
Write multi-line Java strings with text blocks: how the closing delimiter sets the indentation, escapes for trailing spaces and joined lines, and formatted().
What you will learn
- Write a multi-line string as a text block, and say where its content starts and ends
- Predict how much indentation each line keeps from the position of the closing delimiter
- Use \s for trailing spaces, a backslash at the end of a line to join lines, and \""" for three quotes
- Format a template with formatted(), and choose another tool for SQL, HTML and JSON built from outside values
Before you start
What you’ll learn · Before you start
What you will learn
- Write a multi-line string as a text block, and say where its content starts and ends
- Predict how much indentation each line keeps from the position of the closing delimiter
- Use \s for trailing spaces, a backslash at the end of a line to join lines, and \""" for three quotes
- Format a template with formatted(), and choose another tool for SQL, HTML and JSON built from outside values
Before you start
On this page
Written as an ordinary string literal, the one-line JSON template {"name": "%s", "city": "%s"} needs 8 backslashes,
one in front of each double quote, and a three-line HTML e-mail needs a \n at the end of every line. A
text block: A Java string literal that spans several lines: three double quotes and a line break open it, three double quotes close it, and the text between keeps its quotes and line breaks without escapes. Standard since JDK 15. holds such text as written: it opens with three double quotes and a line
break, ends with three more, and keeps quotes and line breaks as they are. Text blocks are standard since JDK 15
(JEP 378). Two rules decide what the string contains: the compiler removes the indentation that only lines the text
up with your code, measured from the least indented line, and it removes spaces at the ends of lines.
You will see both rules with every space made visible, step through where the closing quotes put the margin and why
escapes come last, and use formatted() templates up to where they stop being safe. The exercise is a bill whose
output must match to the last space.
A text block starts on the line after its opening quotes
A text block is an ordinary String, written differently. This e-mail template is one, filled in with formatted():
// An order confirmation e-mail, written as a text block and filled in with formatted().
void main() {
String template = """
<p>Namaste %s,</p>
<p>Your order <b>%s</b> for ₹%d is confirmed.</p>
<p>Delivery to: %s</p>
""";
String email = template.formatted("Asha", "ATF-1042", 1250, "Kothrud, Pune");
IO.print(email);
IO.println("lines: " + email.lines().count() + ", ends with a line break: " + email.endsWith("\n"));
} Output
<p>Namaste Asha,</p> <p>Your order <b>ATF-1042</b> for ₹1250 is confirmed.</p> <p>Delivery to: Kothrud, Pune</p> lines: 3, ends with a line break: true
Recorded with OpenJDK 25.0.4.1 LTS (Eclipse Temurin) on macOS 26 arm64. To run it yourself: java Email.java
formatted("Asha", "ATF-1042", 1250, "Kothrud, Pune")fills the%sand%dplaceholders exactly asString.format(template, ...)would. The last section of the lesson shows where that stops being safe.- Every line ends with
\n, the line feed, even if the source file was saved on Windows with\r\n: the compiler normalises line terminator: The character or pair of characters that ends a line of text: a line feed \n, a carriage return \r, or the pair \r\n that Windows programs write. String.lines() splits at all three and never keeps them. More in “The String API in depth” before anything else. The e-mail has 3 lines and ends with a line break, because the closing"""stands on a line of its own. - The quotes inside the HTML needed no escapes, and neither would quotes in JSON.
The content starts on the line after the opening quotes, and only spaces, tabs and form feeds may stand between the
quotes and that line break. Opening.java asks the JDK’s own compiler, through javax.tools, about three ways of
opening a text block, and prints each opening line with · for a space:
import java.net.URI;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;
// Asks the JDK's own compiler (javax.tools) whether three ways of opening a text block compile, and prints the first
// error javac reports. The class files go to the folder out/.
void main() throws Exception {
Files.createDirectories(Path.of("out"));
check("A", "String s = \"\"\"Namaste\"\"\";");
check("B", "String s = \"\"\" \n Namaste\"\"\";");
check("C", "String s = \"\"\" // greeting\n Namaste\"\"\";");
}
void check(String name, String field) {
String code = "class " + name + " {\n " + field + "\n}\n";
JavaFileObject source = new SimpleJavaFileObject(URI.create("string:///" + name + ".java"), JavaFileObject.Kind.SOURCE) {
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return code;
}
};
var diagnostics = new DiagnosticCollector<JavaFileObject>();
boolean ok = ToolProvider.getSystemJavaCompiler().getTask(null, null, diagnostics, List.of("-d", "out"), null, List.of(source)).call();
String opening = field.substring(field.indexOf("\"\"\""), field.contains("\n") ? field.indexOf('\n') : field.length());
IO.println(opening.replace(" ", "·") + " -> " + (ok ? "compiles" : "error: " + diagnostics.getDiagnostics().getFirst().getMessage(Locale.ROOT)));
} Output
"""Namaste"""; -> error: illegal text block open delimiter sequence, missing line terminator """··· -> compiles """·//·greeting -> error: illegal text block open delimiter sequence, missing line terminator
Recorded with OpenJDK 25.0.4.1 LTS (Eclipse Temurin) on macOS 26 arm64. To run it yourself: java Opening.java
"""Namaste"""; and a comment after the opening quotes are both refused with the same message, while spaces after
them are fine. So a text block can never open and close on one line: for a one-line string, an ordinary literal is
the right tool.
Checkpoint
Check: Opening and closing a text block
The least indented line sets the margin, closing quotes included
The spaces in front of each line serve two purposes. Some only indent the text to match the code around it, and the language calls them incidental white space: The white space in a text block that only lines it up with the code around it: the indentation all its lines share, measured from the least indented line, and spaces at the ends of lines. The compiler removes it.. Others belong to the text, such as the extra indentation of a nested HTML tag, and are essential:
Incidental and essential white space in a text block
Text description of the diagram
The diagram shows the text block String html = """ followed by three lines, each drawn as boxes.
- The first line is 8 spaces followed by <p>Namaste</p>.
- The second line is 8 spaces, then 2 more spaces, then <p>Confirmed</p>.
- The third line is 8 spaces followed by the closing delimiter """;.
A dashed vertical line marks the margin after the first 8 spaces of every line. The 8 spaces in front of the margin are incidental white space, shaded grey and removed by the compiler; the 2 extra spaces of the second line are essential white space, kept. The margin is the indentation of the least indented line, and the closing delimiter's line counts too. Trailing spaces at the ends of lines are removed as well.
The resulting string is two lines: <p>Namaste</p> and a line break, then 2 spaces, <p>Confirmed</p> and a line break. The line break before the closing delimiter ends the last line.
The compiler tells them apart by finding the least indented line, counting the line of the closing """ when it
stands alone, and removing that many white space characters from the start of every line: that amount is the margin.
The program below writes the same two lines four times and moves only the closing delimiter:
// The same two lines four times. Only the place of the closing """ changes, and with it the result.
// visible() shows each space as · and each line break as ⏎, so the whitespace can be counted.
void main() {
String left = """
<p>Namaste</p>
<p>Your order is confirmed.</p>
""";
String aligned = """
<p>Namaste</p>
<p>Your order is confirmed.</p>
""";
String right = """
<p>Namaste</p>
<p>Your order is confirmed.</p>
""";
String sameLine = """
<p>Namaste</p>
<p>Your order is confirmed.</p>""";
IO.println("closing delimiter 4 columns left of the text:");
IO.println(visible(left));
IO.println("closing delimiter under the text:");
IO.println(visible(aligned));
IO.println("closing delimiter 4 columns right of the text:");
IO.println(visible(right));
IO.println("closing delimiter at the end of the last line:");
IO.println(visible(sameLine));
}
String visible(String s) {
return s.replace(" ", "·").replace("\n", "⏎\n");
} Output
closing delimiter 4 columns left of the text: ····<p>Namaste</p>⏎ ······<p>Your·order·is·confirmed.</p>⏎ closing delimiter under the text: <p>Namaste</p>⏎ ··<p>Your·order·is·confirmed.</p>⏎ closing delimiter 4 columns right of the text: <p>Namaste</p>⏎ ··<p>Your·order·is·confirmed.</p>⏎ closing delimiter at the end of the last line: <p>Namaste</p>⏎ ··<p>Your·order·is·confirmed.</p>
Recorded with OpenJDK 25.0.4.1 LTS (Eclipse Temurin) on macOS 26 arm64. To run it yourself: java Delimiter.java
Step through how the compiler finds the margin of each of the four:
The drawing shows the whole scene; its 5 steps are listed below.
Every step as text
- Count the indentation. Both content lines of Delimiter.java are indented in the source: line 1 by 12 spaces, line 2 by 14. The compiler takes the smallest indentation of these lines, and of the closing line when the quotes stand on a line of their own.
- Closing 4 columns left. The closing quotes stand alone, indented 8 spaces. That is now the smallest, so 8 spaces go from every line: line 1 keeps 4 spaces and line 2 keeps 6, as the run's first case printed.
- Closing under the text. Indented 12 spaces, the closing line ties with line 1. 12 spaces go from every line, so line 1 keeps none and line 2 keeps the 2 spaces that indent the nested tag: the usual layout.
- Closing further right. Indented 16 spaces, the closing line is no longer the smallest, so line 1 sets the margin again. The result is the same as with the quotes under the text, as the run's third case shows.
- Closing on the last line. With the quotes at the end of line 2 there is no closing line to count, and the string does not end with a line break. Line 1 sets the margin, 12 spaces, and every result line ends where its text ends.
- Four columns left of the text: the closing line is now the least indented one, so the text keeps 4 extra spaces on every line. Moving the delimiter left is the intended way to indent a whole block.
- Under the text: the first line and the closing line share the smallest indentation, so it all goes and the nested tag keeps its own 2 spaces. This is the usual layout.
- Four columns right of the text: nothing changes, because the text lines are then the least indented.
- At the end of the last line: there is no closing line to count, and the string does not end with a line break, which suits a value such as a single SQL statement or a short message.
Spaces at the end of a line are removed too: they are invisible in an editor, they often arrive with pasted text, and they would silently change the string’s length. When a trailing space matters, write it as an escape, which the next section covers.
Indentation that mixes tabs and spaces is the trap of this rule. Each tab counts as one white space character, the
same as a space, so a line indented with one tab and a line indented with eight spaces have a smallest indentation
of 1: one character is removed from each, and Dosa keeps 7 of its 8 spaces. TabIndent.java shows it with
stripIndent(), the String method that removes incidental white space by the same rule as the compiler, then
compiles the same lines as a text block with javac -Xlint:text-blocks:
import java.net.URI;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;
// Indentation that mixes a tab and spaces: the first line starts with one tab, the second with eight spaces.
// stripIndent() removes incidental white space by the rule the compiler uses for text blocks, and then the JDK's own
// compiler (javax.tools) compiles the same lines as a text block with -Xlint:text-blocks. Class files go to out/.
void main() throws Exception {
String mixed = "\tIdli\n Dosa";
IO.println("stripIndent():");
IO.println(visible(mixed.stripIndent()));
Files.createDirectories(Path.of("out"));
String code = "class Menu {\n String menu = \"\"\"\n" + mixed + "\"\"\";\n}\n";
JavaFileObject source = new SimpleJavaFileObject(URI.create("string:///Menu.java"), JavaFileObject.Kind.SOURCE) {
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return code;
}
};
var diagnostics = new DiagnosticCollector<JavaFileObject>();
boolean ok = ToolProvider.getSystemJavaCompiler()
.getTask(null, null, diagnostics, List.of("-d", "out", "-Xlint:text-blocks"), null, List.of(source))
.call();
IO.println("javac -Xlint:text-blocks Menu.java: " + (ok ? "compiles" : "does not compile") + ", with "
+ diagnostics.getDiagnostics().size() + " warning");
for (var d : diagnostics.getDiagnostics()) {
IO.println(" Menu.java:" + d.getLineNumber() + ": " + d.getKind().toString().toLowerCase(Locale.ROOT) + ": "
+ d.getMessage(Locale.ROOT));
}
}
String visible(String s) {
return s.replace(" ", "·").replace("\t", "→").replace("\n", "⏎\n");
} Output
stripIndent(): Idli⏎ ·······Dosa javac -Xlint:text-blocks Menu.java: compiles, with 1 warning Menu.java:2: warning: inconsistent white space indentation
Recorded with OpenJDK 25.0.4.1 LTS (Eclipse Temurin) on macOS 26 arm64. To run it yourself: java TabIndent.java
The warning says that the margin the compiler found is probably not the one you meant. Indent text blocks with spaces only, which most editors can be set to do.
Key idea
The margin is the indentation of the least indented line, the closing quotes’ line included when they stand alone: move the quotes left to keep indentation, and put them at the end of the last line to drop the final line break.
Checkpoint
Check: The margin and the closing quotes
Escapes are applied last, after the margin is removed
All the escapes of ordinary string literals work in text blocks, and two arrived with them in JDK 15: \s, a space
that survives the removal of trailing white space (ordinary literals accept it too), and a backslash at the very end
of a line, a line continuation: A backslash at the very end of a line in a text block, an escape that stands for nothing: it joins the line to the next one, so a long line of source can stay one line in the string., which joins the line to the next one and exists
only in text blocks. Both appear in this form:
// The escape sequences that matter most in text blocks. visible() shows each space as ·, each tab as → and each
// line break as ⏎, so the white space can be counted.
void main() {
String form = """
Name:\s\s
City: Pune, \
Maharashtra
Says: \"""Hello\"""
Tab:\tafter a tab
""";
IO.print(visible(form));
IO.println("length " + form.length() + ", lines " + form.lines().count());
// A Unicode escape is translated before the compiler reads the text block, so it is an ordinary space by then,
// and a space at the end of a line is removed.
String unicode = """
Name:\u0020\u0020
""";
IO.print(visible(unicode));
}
String visible(String s) {
return s.replace(" ", "·").replace("\t", "→").replace("\n", "⏎\n");
} Output
Name:··⏎ City:·Pune,·Maharashtra⏎ Says:·"""Hello"""⏎ Tab:→after·a·tab⏎ length 67, lines 4 Name:⏎
Recorded with OpenJDK 25.0.4.1 LTS (Eclipse Temurin) on macOS 26 arm64. To run it yourself: java Escapes.java
Name:\s\skept its two trailing spaces as··.City: Pune, \followed by a line break became one line,City: Pune, Maharashtra: the way to break a long line in the source without breaking it in the string.- Three quotes in a row would close the block, so one of them needs a backslash:
\"""produces""". \tproduced a real tab, shown as→.
The order of the compiler’s work explains every line of that output. The language specification gives three steps, and the escapes come last. Step through them on the form:
The drawing shows the whole scene; its 5 steps are listed below.
Every step as text
- As written. The form has five source lines, each indented 12 spaces, with the closing quotes under them. Name:\s\s ends with two escapes, and City: Pune, \ ends with a backslash.
- Step 1, line ends. Every line terminator becomes a single line feed, \n, whatever the file was saved with. A file saved on Windows, with CR LF at the end of each line, gives the same string as one saved on Linux.
- Step 2, the margin. Incidental white space goes, as stripIndent removes it: the 12-space margin from every line, and spaces at the ends of lines. The escapes are still plain chars here, a backslash and an s, so nothing after them is stripped.
- Step 3, the escapes. Now the escapes are read: each \s becomes a space, the backslash at the end of the City line joins it to the next line, \" becomes a quote and \t a tab.
- The String. The result has 4 lines and 67 chars, as the run printed: Name: with its two spaces, City: Pune, Maharashtra on one line, three quotes either side of Hello, and a real tab after Tab:.
Because escapes are applied after the margin is counted and the line ends are stripped, an escaped tab or line break
never changes the indentation, and \s works as a fence that keeps the spaces before it. A
Unicode escape: A backslash, the letter u and four hexadecimal digits, such as \u000a, that stands for one character. The compiler replaces every one in a source file before it looks for comments, so \u000a in a comment is a real line break. More in “Comments, Javadoc and naming conventions” such as \u0020 is different: the compiler translates it before it
reads the text block at all, so it is an ordinary space by then, and the run’s last line, Name:⏎, shows both of them
removed as trailing white space.
Checkpoint
Check: Escapes, applied last
formatted() fills a template, unescaped and in the default locale
formatted(args) is String.format(this, args) written the other way round, so a text block with placeholders is a
handy template for messages, reports and test data. The template is a format string: The text that printf, String.format and formatted fill with values: ordinary characters are copied as they are, and each % starts a format specifier, such as %d or %.2f, that is replaced by the next value. More in “Formatted output with printf and formatted”,
and two cautions come with it.
First, it formats numbers and dates with the computer’s default locale: The locale the JVM takes from the computer's settings when it starts, which String.format, printf and formatted use whenever no Locale is passed. The options -Duser.language and -Duser.country set it. More in “Formatted output with printf and formatted”, so %,d
prints 125,000 in one country and 125.000 in another:
// formatted() fills a template with the default locale, so a %,d grouping depends on the computer's settings.
// String.format with an explicit locale gives the same text everywhere.
void main() {
String template = """
Order ATF-1042: ₹%,d""";
for (Locale where : new Locale[] {Locale.US, Locale.GERMANY}) {
Locale.setDefault(where);
IO.println("default " + where.toLanguageTag() + ": " + template.formatted(125000));
}
IO.println("Locale.ROOT, any default: " + String.format(Locale.ROOT, template, 125000));
} Output
default en-US: Order ATF-1042: ₹125,000 default de-DE: Order ATF-1042: ₹125.000 Locale.ROOT, any default: Order ATF-1042: ₹125,000
Recorded with OpenJDK 25.0.4.1 LTS (Eclipse Temurin) on macOS 26 arm64. To run it yourself: java TemplateLocale.java
When the output must be the same everywhere, call String.format(Locale.ROOT, template, args), which takes the locale
explicitly, as the run’s last line does.
Second, and more serious, it inserts values exactly as they are:
// A JSON request body built with formatted(). It is correct until a value contains a double quote.
void main() {
String body = """
{"name": "%s", "city": "%s"}""";
IO.println(body.formatted("Asha Rao", "Pune"));
IO.println(body.formatted("Asha \"Ash\" Rao", "Pune"));
} Output
{"name": "Asha Rao", "city": "Pune"}
{"name": "Asha "Ash" Rao", "city": "Pune"}
Recorded with OpenJDK 25.0.4.1 LTS (Eclipse Temurin) on macOS 26 arm64. To run it yourself: java JsonBreak.java
The second line is not valid JSON. Step through why, from the template to the point where a JSON reader gives up:
The drawing shows the whole scene; its 5 steps are listed below.
Every step as text
- The template. The text block holds one line, {"name": "%s", "city": "%s"}. formatted() replaces each %s with the text of the next value, exactly as String.format would.
- The value. The first value is the name Asha "Ash" Rao. It holds two double quotes, which mean something to JSON: a string begins and ends with a quotation mark.
- formatted() pastes it in. formatted() knows nothing about JSON, so the name goes in as it is. The line becomes {"name": "Asha "Ash" Rao", "city": "Pune"}, the second line the run printed.
- The string ends early. A JSON reader starts the name's string at its first quote and ends it at the next one, after Asha and a space. In JSON a quote inside a string must be written with a backslash, as \".
- The reader gives up. After a value inside an object, JSON allows only a comma or a closing brace. The reader finds Ash instead, so the whole document is rejected, although the template was right.
The same happens to HTML when a value contains < or &, and to SQL when a value contains a quote, where it becomes
a security hole called
A flaw in which text from outside a program becomes part of an SQL statement, so a crafted value can change what the statement does. Binding the value with a PreparedStatement keeps it data..
Templates are for text you wrote
Never build SQL, HTML or JSON by formatting values you did not write yourself: use a PreparedStatement for SQL
(the JDBC lessons in the back-end module show how), escape values for HTML, and let a JSON library write JSON.
Checkpoint
Check: Templates with formatted()
Practise: a bill that matches to the last space
Bill.java holds the method to write and two checks that compare its output with the expected bill exactly, spaces
and line breaks included. Each item line is the name padded to 16 characters, an x and the quantity padded to 3,
and the amount right-aligned in 5 characters. As it starts, bill returns an empty string, and the checks show the
expected text with · for each space:
// Your turn: write bill() so that it returns exactly the text the checks expect, every space and line break included.
// Each item line is the name padded to 16 characters, "x" and the quantity padded to 3, and the amount (quantity times
// price) right-aligned in 5 characters. A text block makes a good template for the whole bill.
String bill(int table, String[] names, int[] quantities, int[] prices) {
return ""; // Write your code here.
}
int failed = 0;
void check(String got, String expected) {
if (!got.equals(expected)) {
failed++;
IO.println("FAIL: the bill differs. Expected, with · for each space and ⏎ for each line break:");
IO.print(visible(expected));
IO.println("got:");
IO.print(visible(got));
}
}
String visible(String s) {
return s.isEmpty() ? "(nothing)\n" : s.replace(" ", "·").replace("\n", "⏎\n") + (s.endsWith("\n") ? "" : "\n");
}
void main() {
check(bill(4, new String[] {"Masala dosa", "Filter coffee"}, new int[] {2, 2}, new int[] {90, 30}), """
Canteen bill, table 4
---------------------
Masala dosa x2 ₹180
Filter coffee x2 ₹60
Total ₹240
""");
check(bill(7, new String[] {"Idli", "Vada", "Lemon rice"}, new int[] {3, 2, 1}, new int[] {20, 25, 70}), """
Canteen bill, table 7
---------------------
Idli x3 ₹60
Vada x2 ₹50
Lemon rice x1 ₹70
Total ₹180
""");
IO.println(failed + " of 2 checks failed");
if (failed > 0) {
System.exit(1);
}
} Output (exit status 1)
FAIL: the bill differs. Expected, with · for each space and ⏎ for each line break: Canteen·bill,·table·4⏎ ---------------------⏎ Masala·dosa·····x2···₹180⏎ Filter·coffee···x2····₹60⏎ Total················₹240⏎ got: (nothing) FAIL: the bill differs. Expected, with · for each space and ⏎ for each line break: Canteen·bill,·table·7⏎ ---------------------⏎ Idli············x3····₹60⏎ Vada············x2····₹50⏎ Lemon·rice······x1····₹70⏎ Total················₹180⏎ got: (nothing) 2 of 2 checks failed
Recorded with OpenJDK 25.0.4.1 LTS (Eclipse Temurin) on macOS 26 arm64. To run it yourself: java Bill.java
You are done when it prints 0 of 2 checks failed. Hints: make the bill a text block with three placeholders, the
table number, the item lines and the total, and end the line of the item placeholder with a backslash, so that
Total follows the item lines directly. Build each item line with "%-16sx%-3d%5s\n".formatted(...), passing
"₹" + amount as the last value. Use \n rather than %n, which is \r\n on Windows and would make the bill
differ there.
Then run through the four ideas, from the opening quotes to formatted(): the first question is on the idea you know
least, and every choice carries its own reason.
Practice that adapts to you
Practise this lesson
10 questions about 4 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Fresher · What is incidental white space in a text block, and how does the closing delimiter affect it?
Incidental white space is the indentation that only lines a text block up with the surrounding code, as opposed to
essential white space, which belongs to the text, such as the extra indentation of a nested tag. The compiler finds
the smallest indentation among the non-blank lines of the content and the line of the closing delimiter, if that
stands on a line of its own, and removes that many white space characters from the start of every line; it also
removes trailing white space from each line. So the closing """ matters: placed under the text, it changes
nothing; moved four columns to the left, it leaves four extra spaces on every line; placed at the end of the last
line, it ends the string without a line break. A good answer mentions \s for a trailing space that must stay.
Expect the interviewer to follow up with code to work out by hand. Here are two such questions, with the reasoning after each:
Checkpoint
Interview round: two follow-up questions
Two questions in the module bank need a longer answer: a search box that fills an SQL template, and a report test that fails on one build computer only.
Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.
-
Fresher to mid
In a code review you find a text block holding SELECT name, fare FROM trains WHERE city = '%s', filled in with formatted(city), where city comes from a search box. The tests pass. What do you say, and how would you rewrite it while keeping the text block?
-
Mid to senior
A test compares a generated report with an expected text block and fails only on the Windows build agent, although the two texts look identical in the failure message. What do you check, in what order, and how do you make the test reliable without hiding real differences?
Key takeaways
- A text block opens with
"""and a line break, ends with""", and is an ordinaryStringconstant; only white space may follow the opening quotes on their line. - The compiler normalises line endings to
\n, then removes the incidental indentation measured from the least indented line, the closing delimiter’s line included, and the spaces at the ends of lines. - Move the closing delimiter left to keep indentation, and put it at the end of the last line to drop the final line break; a tab counts as one space, so mixed indentation strips too little.
\skeeps a space, a backslash at the end of a line joins it to the next, and\"""writes three quotes, because escapes are applied after the indentation is removed.formatted()uses the default locale and inserts values unescaped: never build SQL, HTML or JSON from outside values with it.
Terms in this lesson
- incidental white space
- The white space in a text block that only lines it up with the code around it: the indentation all its lines share, measured from the least indented line, and spaces at the ends of lines. The compiler removes it.
- line continuation
- A backslash at the very end of a line in a text block, an escape that stands for nothing: it joins the line to the next one, so a long line of source can stay one line in the string.
- text block
- A Java string literal that spans several lines: three double quotes and a line break open it, three double quotes close it, and the text between keeps its quotes and line breaks without escapes. Standard since JDK 15.
References
- JEP 378: Text Blocks (OpenJDK)
- Programmer's Guide to Text Blocks (Java SE 25) (Oracle)
- The Java Language Specification, Java SE 25: 3.10.6 Text Blocks and 3.10.7 Escape Sequences (Oracle)
- The Java Language Specification, Java SE 25: 3.3 Unicode Escapes (Oracle)
- String.stripIndent, translateEscapes and formatted (Java SE 25 API) (Oracle)
- The javac command: -Xlint:text-blocks (JDK 25 tool specifications) (Oracle)
- javax.tools.ToolProvider and JavaCompiler (Java SE 25 API) (Oracle)
- Formatter (Java SE 25 API) (Oracle)
- RFC 8259: The JavaScript Object Notation (JSON) Data Interchange Format (IETF)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress