你还在用 `+ “\\n“` 拼多行字符串吗?Java 的文本块都已经能让代码“像人写的”了!

👋 你好,欢迎来到我的博客!我是【菜鸟不学编程】
   我是一个正在奋斗中的职场码农,步入职场多年,正在从“小码农”慢慢成长为有深度、有思考的技术人。在这条不断进阶的路上,我决定记录下自己的学习与成长过程,也希望通过博客结识更多志同道合的朋友。
  
  🛠️ 主要方向包括 Java 基础、Spring 全家桶、数据库优化、项目实战等,也会分享一些踩坑经历与面试复盘,希望能为还在迷茫中的你提供一些参考。
  💡 我相信:写作是一种思考的过程,分享是一种进步的方式。
  
   如果你和我一样热爱技术、热爱成长,欢迎关注我,一起交流进步!

I. 文本块语法:""" 多行字符串 """

文本块最直观的优点就是:多行就是多行,不需要你手动写换行符。

1)基本用法:三引号包起来

String sql = """
    SELECT id, name, created_at
    FROM users
    WHERE status = 'ACTIVE'
    ORDER BY created_at DESC
    """;

你看,字符串里有换行、缩进,代码也有换行、缩进——读起来像你脑子里想的样子

2)起始行的“小规矩”

  • 开始的 """ 后面通常换行(更清晰)
  • 结束的 """ 所在位置会影响缩进裁剪(下一节讲)

II. 转义与缩进:自动处理空格(但别误会它“会读心”)

文本块有两个“真香点”:

  1. 自动处理缩进(indentation stripping)
  2. 减少转义需求

1)缩进裁剪:它会找“最小公共缩进”

看这个例子:

String s = """
        line1
          line2
        line3
        """;
System.out.println(s);

输出会保留相对缩进,但会裁掉“所有行共有的最小缩进”。
这意味着:你可以为了让代码排版好看而缩进,但最终字符串不会多出那些“为了对齐而产生的多余空格”。

重点:它保留相对缩进,不是把所有行都左对齐。

2)想控制末尾换行?用 \(行尾反斜杠)

文本块默认会包含结尾换行(多数时候是你想要的)。
但如果你不想要最后那个换行:

String noTrailingNewline = """
    hello
    world\
    """;

这个 \ 会把最后的换行“吃掉”。
(第一次见的人一般会“啊?”一下,正常😄)

3)需要在行尾保留空格?用 \s

有时候你想保留行尾空格(例如 Markdown 对齐、某些固定格式输出),文本块会帮你裁剪得太“干净”。这时你可以显式保留:

String keepSpace = """
    a\s
    b\s
    """;

III. 格式化:String::formatted 方法(终于不用满屏 + 了)

文本块和 formatted() 简直是天生一对:
你写一个模板,再把变量填进去,代码立刻从“拼接现场”变成“模板渲染”。

1)示例:生成一段 JSON

String name = "Ophelia";
int score = 97;

String json = """
    {
      "name": "%s",
      "score": %d,
      "passed": %b
    }
    """.formatted(name, score, score >= 60);

System.out.println(json);

你会发现它比 String.format(...) 更顺手的地方在于:
模板字符串就在那儿,你一眼能看到结构,不用在参数列表里找半天。

当然,它底层还是走 format 语义,格式符 %s/%d/%f 那些规则都一样。

2)小心“格式化注入”(别在高风险输入上裸奔)

如果用户输入里可能包含 %,在某些场景会造成格式化异常或输出混乱。
高风险场景请先转义或用更安全的模板渲染方式(尤其是日志/SQL/命令行这类)。

IV. 与传统字符串比较:可读性提升(不是“语法糖”,是“维护成本下降”)

我们来对比一下经典的“字符串地狱”。

1)传统写法:拼接 + 转义 + 换行符

String html =
    "<html>\n" +
    "  <body>\n" +
    "    <h1>Report</h1>\n" +
    "  </body>\n" +
    "</html>\n";

读起来像在数 \n,写起来像在搬砖,还容易漏掉引号。

2)文本块写法:结构就是结构

String html = """
    <html>
      <body>
        <h1>Report</h1>
      </body>
    </html>
    """;

维护体验差别非常大

  • 改一行 HTML,不会牵动 10 行拼接
  • diff 更干净,代码审查也更容易
  • 少了很多“我是不是漏了个 +”的低级错误

所以它不是仅仅“少写几个字符”,而是:降低未来维护者(包括你自己)脑内负担

V. HTML/JSON 应用:嵌入多行文本(最常见、最值得)

文本块最舒服的用途就是放这些“结构化文本”:

1)SQL:别再在字符串里“逃逸人生”

String sql = """
    SELECT id, name
    FROM users
    WHERE age >= %d
    ORDER BY name
    """.formatted(18);

2)JSON:结构清晰,编辑成本低

String payload = """
    {
      "event": "login",
      "userId": "%s",
      "timestamp": "%s"
    }
    """.formatted("u-42", java.time.Instant.now());

3)HTML:用于邮件模板/报表渲染

String page = """
    <div class="card">
      <h2>%s</h2>
      <p>%s</p>
    </div>
    """.formatted("Weekly Report", "All systems nominal.");

但我得提醒一句:如果你在做复杂 HTML 模板,文本块只是“字符串更好写”,它不是模板引擎。复杂场景建议用真正的模板方案(比如 Thymeleaf/Freemarker 等),别硬撑🙂。

VI. 项目:生成报告的文本块使用(一个“像工程”的小报表)

我们做个小项目:生成一份“运行报告”,同时输出 MarkdownHTML 两种格式。
为什么是这两种?因为它们最能体现文本块的价值:结构化、多行、可读。

1)定义一个简单的数据模型

import java.time.Instant;
import java.util.List;

public record Report(
        String title,
        Instant generatedAt,
        List<Item> items
) {
    public record Item(String name, String status, long latencyMs) {}
}

2)用文本块生成 Markdown 报告

import java.util.stream.Collectors;

public class ReportRenderer {

    public static String toMarkdown(Report r) {
        String rows = r.items().stream()
                .map(i -> "| %s | %s | %dms |".formatted(i.name(), i.status(), i.latencyMs()))
                .collect(Collectors.joining("\n"));

        return """
            # %s

            - Generated At: %s
            - Total Items: %d

            | Name | Status | Latency |
            | ---|--------|---------|
            %s

            > Note: Latency is measured at client side.
            """.formatted(
                r.title(),
                r.generatedAt(),
                r.items().size(),
                rows
        );
    }
}

你会发现:Markdown 的“表格块”在文本块里写起来特别顺,不会被 \n 折磨。

3)用文本块生成 HTML 报告(简单版)

import java.util.stream.Collectors;

public class HtmlReportRenderer {

    public static String toHtml(Report r) {
        String rows = r.items().stream()
                .map(i -> """
                    <tr>
                      <td>%s</td>
                      <td>%s</td>
                      <td>%d ms</td>
                    </tr>
                    """.formatted(escape(i.name()), escape(i.status()), i.latencyMs()))
                .collect(Collectors.joining("\n"));

        return """
            <!doctype html>
            <html lang="en">
              <head>
                <meta charset="utf-8"/>
                <title>%s</title>
                <style>
                  body { font-family: Arial, sans-serif; margin: 24px; }
                  table { border-collapse: collapse; width: 100%%; }
                  th, td { border: 1px solid #ddd; padding: 8px; }
                  th { text-align: left; }
                </style>
              </head>
              <body>
                <h1>%s</h1>
                <p><b>Generated At:</b> %s</p>
                <table>
                  <thead>
                    <tr><th>Name</th><th>Status</th><th>Latency</th></tr>
                  </thead>
                  <tbody>
                    %s
                  </tbody>
                </table>
              </body>
            </html>
            """.formatted(
                escape(r.title()),
                escape(r.title()),
                r.generatedAt(),
                rows
        );
    }

    private static String escape(String s) {
        if (s == null) return "";
        return s.replace("&", "&amp;")
                .replace("<", "&lt;")
                .replace(">", "&gt;")
                .replace("\"", "&quot;");
    }
}

注意一个小细节:CSS 里有 100%,在 formatted() 模板中 % 是格式符起始,所以要写 100%%
这类“百分号小坑”在模板里很常见,别踩一次才记住😅。

4)跑一个 demo(生成两份报告字符串)

import java.time.Instant;
import java.util.List;

public class ReportDemo {
    public static void main(String[] args) {
        Report report = new Report(
                "Service Health Report",
                Instant.now(),
                List.of(
                        new Report.Item("UserService", "OK", 23),
                        new Report.Item("OrderService", "WARN", 187),
                        new Report.Item("PaymentService", "FAIL", 0)
                )
        );

        String md = ReportRenderer.toMarkdown(report);
        String html = HtmlReportRenderer.toHtml(report);

        System.out.println(md);
        System.out.println("------");
        System.out.println(html.substring(0, Math.min(400, html.length())) + "...");
    }
}

小结:文本块不是“更短”,是“更不容易写错”

Java 文本块最值钱的地方不是省了多少字符,而是:

  • 结构可读:HTML/JSON/SQL 这类文本“长得像它本来的样子”
  • 少转义:减少 \n\t\" 这种视觉噪音
  • 更好维护:代码 diff 更干净,评审更轻松
  • 搭配 formatted:模板化输出更自然

最后我想用一句反问收尾(你应该已经习惯我这种嘴欠式结尾了😄):
你写的多行字符串,是在表达“内容”,还是在表达“转义规则”?
如果答案是后者——那文本块真的该用起来了🙂。

📝 写在最后

如果你觉得这篇文章对你有帮助,或者有任何想法、建议,欢迎在评论区留言交流!你的每一个点赞 👍、收藏 ⭐、关注 ❤️,都是我持续更新的最大动力!

我是一个在代码世界里不断摸索的小码农,愿我们都能在成长的路上越走越远,越学越强!

感谢你的阅读,我们下篇文章再见~👋

✍️ 作者:某个被流“治愈”过的 Java 老兵
📅 日期:2025-08-25
🧵 本文原创,转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值