Text Blocks, finalized in Java 15 and available in Java 17 through JEP 378, revolutionize how we work with multi-line strings in Java. Using triple-double quotes ("""), text blocks make it easy to write readable, maintainable strings for JSON, SQL, HTML, XML, and other multi-line content.
The Problem with Traditional Strings
Before text blocks, multi-line strings required extensive use of escape sequences and concatenation:
String json = "{\n" +
" \"name\": \"John Doe\",\n" +
" \"age\": 30,\n" +
" \"email\": \"john@example.com\"\n" +
"}";
String sql = "SELECT users.name, users.email, orders.total\n" +
"FROM users\n" +
"JOIN orders ON users.id = orders.user_id\n" +
"WHERE orders.status = 'completed'\n" +
"ORDER BY orders.total DESC";
This approach is:
- Hard to read: Escape sequences clutter the code
- Error-prone: Easy to forget
\nor closing quotes - Difficult to maintain: Formatting changes require updating multiple lines
Text Blocks to the Rescue
Text blocks use triple-double quotes and preserve formatting automatically:
String json = """
{
"name": "John Doe",
"age": 30,
"email": "john@example.com"
}
""";
String sql = """
SELECT users.name, users.email, orders.total
FROM users
JOIN orders ON users.id = orders.user_id
WHERE orders.status = 'completed'
ORDER BY orders.total DESC
""";
Much cleaner and more readable!
How Text Blocks Work
Basic Syntax
A text block starts with """ followed by a line terminator, and ends with """:
String greeting = """
Hello,
World!
""";
Important: The opening """ must be followed by a line terminator. This is invalid:
String invalid = """Hello"""; // ✗ Compile error
Automatic Indentation Management
Text blocks automatically remove common leading whitespace:
public class Example {
public void printMessage() {
String message = """
This is indented in the code,
but the common indentation is removed
from the final string.
""";
System.out.println(message);
}
}
Output:
This is indented in the code,
but the common indentation is removed
from the final string.
The closing """ position determines the indentation baseline:
// Closing delimiter at the start - removes all indentation
String noIndent = """
Line 1
Line 2
""";
// Closing delimiter indented - preserves relative indentation
String withIndent = """
Line 1
Line 2
""";
Real-World Examples
1. JSON Configuration
public class ConfigGenerator {
public String generateDatabaseConfig(String host, int port, String database) {
return """
{
"database": {
"host": "%s",
"port": %d,
"name": "%s",
"pool": {
"minSize": 5,
"maxSize": 20,
"timeout": 30000
}
}
}
""".formatted(host, port, database);
}
public String generateUserJson(String name, String email, List<String> roles) {
String rolesJson = roles.stream()
.map(role -> "\"" + role + "\"")
.collect(Collectors.joining(", "));
return """
{
"user": {
"name": "%s",
"email": "%s",
"roles": [%s],
"active": true
}
}
""".formatted(name, email, rolesJson);
}
}
2. SQL Queries
public class UserRepository {
public String getUsersWithOrdersQuery() {
return """
SELECT
u.id,
u.name,
u.email,
COUNT(o.id) as order_count,
SUM(o.total) as total_spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.active = true
GROUP BY u.id, u.name, u.email
HAVING COUNT(o.id) > 0
ORDER BY total_spent DESC
LIMIT 100
""";
}
public String getComplexReportQuery(String startDate, String endDate) {
return """
WITH monthly_sales AS (
SELECT
DATE_TRUNC('month', order_date) as month,
SUM(total) as revenue
FROM orders
WHERE order_date BETWEEN '%s' AND '%s'
GROUP BY month
)
SELECT
month,
revenue,
LAG(revenue) OVER (ORDER BY month) as prev_month_revenue,
revenue - LAG(revenue) OVER (ORDER BY month) as growth
FROM monthly_sales
ORDER BY month DESC
""".formatted(startDate, endDate);
}
}
3. HTML Templates
public class EmailTemplateGenerator {
public String generateWelcomeEmail(String userName, String activationLink) {
return """
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; }
.container { max-width: 600px; margin: 0 auto; }
.button {
background-color: #4CAF50;
color: white;
padding: 15px 32px;
text-decoration: none;
display: inline-block;
}
</style>
</head>
<body>
<div class="container">
<h1>Welcome, %s!</h1>
<p>Thank you for joining our platform.</p>
<p>Please click the button below to activate your account:</p>
<a href="%s" class="button">Activate Account</a>
<p>If you didn't create this account, please ignore this email.</p>
</div>
</body>
</html>
""".formatted(userName, activationLink);
}
}
4. XML Documents
public class XmlGenerator {
public String generateProductXml(String id, String name, double price, List<String> categories) {
String categoriesXml = categories.stream()
.map(cat -> " <category>" + cat + "</category>")
.collect(Collectors.joining("\n"));
return """
<?xml version="1.0" encoding="UTF-8"?>
<product>
<id>%s</id>
<name>%s</name>
<price currency="USD">%.2f</price>
<categories>
%s
</categories>
</product>
""".formatted(id, name, price, categoriesXml);
}
}
String Interpolation with formatted()
Text blocks work seamlessly with the formatted() method (or String.format()):
String name = "Alice";
int age = 30;
String message = """
User Profile:
-------------
Name: %s
Age: %d
Status: %s
""".formatted(name, age, age >= 18 ? "Adult" : "Minor");
Escape Sequences Still Work
You can still use escape sequences when needed:
String withEscapes = """
Line 1\tTabbed
Line 2\t\tDouble tabbed
Quote: \"Hello\"
Backslash: \\
""";
Preserving Trailing Whitespace
Use \s to preserve trailing spaces:
String withTrailingSpace = """
This line has trailing space\s
This line doesn't
""";
Line Continuation
Use \ at the end of a line to join it with the next:
String oneLine = """
This is all \
one \
line.
""";
// Result: "This is all one line."
Best Practices
1. Position the Closing Delimiter Carefully
// Good - closing delimiter determines baseline indentation
String good = """
Content here
More content
""";
// Also good - explicit about no indentation
String alsoGood = """
Content here
More content
""";
2. Use for Multi-Line Content Only
// Don't use text blocks for single-line strings
String bad = """
Just one line
""";
// Use regular strings instead
String good = "Just one line";
3. Combine with String Methods
String template = """
{
"name": "%s",
"value": %d
}
""";
String json = template.formatted("example", 42)
.trim()
.replaceAll("\\s+", " ");
Conclusion
Text Blocks are a game-changer for working with multi-line strings in Java. They make code more readable, reduce errors, and simplify maintenance. Whether you’re working with JSON, SQL, HTML, or any other multi-line format, text blocks provide a clean, intuitive syntax that feels natural and keeps your code organized.