我们最近启动的新项目,统一都用上了 JDK21。说起来,现在都 2025 年了,JDK24都出来了,我们还在用JDK21,更魔幻的是有的人还把JDK8引入的特性当成新特性…
其实有些早就发布、但特别实用的新特性,至今还有很多人没真正用过。今天就想聊一个对写代码挺有帮助的功能:文本块 。熟悉的朋友可能早已用得顺手了,不熟的朋友可能连它长什么样都还不知道。
对 Java 开发者来说,多行字符串一直是个难题。无论是写 SQL、拼 JSON、嵌 HTML,还是搞个配置文件,只要一多行,屏幕上立马就堆满了拼接符、转义字符,还有那乱七八糟的缩进,改起来很麻烦。
现在,Java 给我们带来了一个更优雅的解决方案: Text Blocks(文本块) 。有了它,处理多行字符串终于不再那么头疼,代码更清爽,可读性也高了不少。
1. 什么是 Text Blocks(文本块)?
Text Blocks 最早是在 Java 13 以预览功能亮相,Java 14 做了些改进,到 Java 15 正式成为标准。它的核心目标很简单:让我们可以直接、干净地写多行字符串,不再被各种引号、加号和转义搞得焦头烂额。
它的语法也很直观:用三个双引号("""
)包裹内容,就能写出结构清晰、格式保持原样的多行文本。不用拼、不用转义,直接贴上去就行。
以下是一个最基本的文本块的使用示例:
1 2 3 4 5 String textBlock = """ 这是一个文本块 跨越多行 保留了格式 """ ;
主要结构如下:
起始定界符 :必须是三个双引号("""
),而且后面要紧跟一个换行,不能省。
内容部分 :可以写在多行上,怎么换行、怎么缩进,全都按你写的来保留原样。
结束定界符 :同样是 """
,可以直接写在内容最后一行的末尾,也可以另起一行单独写,灵活处理。
以下都是语法正确的写法:
1 2 3 4 5 6 7 8 9 10 11 12 String example1 = """ Hello World """ ;String example2 = """ Hello World""" ;String empty = """ """ ;
2. 基本用法
2.1. JSON 更自然地嵌入代码
传统写法,用传统方式拼 JSON,换行全靠 \n
,字符串拼接像堆积木,格式难对齐,改字段尤其麻烦,稍不留神就漏了逗号或引号:
1 2 3 4 5 6 7 String json = "{\n" + " \"id\": 101,\n" + " \"name\": \"Wireless Mouse\",\n" + " \"price\": 29.99,\n" + " \"inStock\": true,\n" + " \"tags\": [\"electronics\", \"accessories\", \"usb\"]\n" + "}" ;
使用 Text Blocks 之后,看看这个版本,和你在 Postman 或 JSON 编辑器里看到的格式几乎一模一样,结构清晰、一眼能看懂。复制、调试、修改都方便,特别适合构造请求体、模拟响应或做配置模板:
1 2 3 4 5 6 7 8 9 String json = """ { "id": 101, "name": "Wireless Mouse", "price": 29.99, "inStock": true, "tags": ["electronics", "accessories", "usb"] } """ ;
2.2. HTML Templates
传统写法,拼 HTML 的方式,不仅看起来乱,每一行都被双引号框着、加号连着,还容易因为忘记转义或少拼一个变量而报错。页面结构根本看不清,调试起来很费劲:
1 2 3 4 5 6 7 8 9 10 String html = "<html>\n" + " <head>\n" + " <title>Product Detail</title>\n" + " </head>\n" + " <body>\n" + " <h1>" + productName + "</h1>\n" + " <p>价格:¥" + price + "</p>\n" + " <p>库存状态:" + stockStatus + "</p>\n" + " </body>\n" + "</html>" ;
使用 Text Blocks 之后,HTML 写得像模板一样自然,变量通过 .formatted()
插入,逻辑清晰、结构分明。无论是动态生成页面、预览邮件内容,还是写测试输出,这种方式都省心不少:
1 2 3 4 5 6 7 8 9 10 11 12 String html = """ <html> <head> <title>Product Detail</title> </head> <body> <h1>%s</h1> <p>价格:¥%s</p> <p>库存状态:%s</p> </body> </html> """ .formatted(productName, price, stockStatus);
2.3. SQL 查询
虽然很少在代码中这么写sql,但是还是写下这种用法。
传统写法,拼接符满天飞,格式乱糟糟,看久了眼疼。每次改列名或者调整顺序都得小心翼翼:
1 2 3 4 5 6 7 String sql = "SELECT customer_id, COUNT(order_id) AS order_count, " + " SUM(total_amount) AS total_spent " + "FROM orders " + "WHERE order_date BETWEEN ? AND ? " + " AND status = 'COMPLETED' " + "GROUP BY customer_id " + "ORDER BY total_spent DESC" ;
使用 Text Blocks 之后,整个查询语句就像直接复制粘贴自数据库客户端一样直观,结构清晰,不再被格式和语法干扰,大大提升了可读性和维护效率。
1 2 3 4 5 6 7 8 9 String sql = """ SELECT customer_id, COUNT(order_id) AS order_count, SUM(total_amount) AS total_spent FROM orders WHERE order_date BETWEEN ? AND ? AND status = 'COMPLETED' GROUP BY customer_id ORDER BY total_spent DESC """ ;
3. 高级特性与能力
Text Blocks 搭配字符串格式化,让模板更灵活
Text Blocks 可以轻松和 String.format()
或 .formatted()
方法结合,用来动态插入变量,非常适合写邮件模板、系统通知等:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 String template = """ 亲爱的 %s: 您的订单(编号:%s)已成功付款 ¥%.2f。 我们将在 %s 前完成发货,请留意物流信息。 感谢您的支持! —— 商城团队 """ ;String message = String.format(template, "李明" , "A20230721" , 259.80 , "2025-07-25" );String quickMsg = """ 您好 %s, 您当前积分为:%d 分。 """ .formatted("小张" , 820 );
Text Blocks 中的特殊字符处理
大多数情况下你不需要再用 \n
、\"
这些转义符,但有时仍然会用到,比如需要制表符、斜杠或三引号的情况:
1 2 3 4 5 6 7 String log = """ 第1行 第2行,含制表符:\t(对齐用) 第3行,带引号:"不需转义" 第4行,路径示例:C:\\Users\\Documents 第5行,包含三引号:\""" """ ;
缩进处理
Text Blocks 会自动识别和剥离共同缩进,让你专注内容本身,不用手动对齐每一行:
1 2 3 4 5 6 7 8 9 10 11 12 public class DemoIndent { public void show () { String snippet = """ <div> <p>Hello, Text Blocks!</p> </div> """ ; System.out.println(snippet); } }
使用 indent()
方法手动添加缩进
虽然 Text Blocks 会自动处理缩进,但你也可以用 .indent()
增加额外缩进(比如嵌入模板、展示源码等场景):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 String content = """ 标题:使用 Text Blocks 内容行 1 内容行 2(有缩进) 结尾 """ ;String indented = content.indent(2 );String result = """ 示例开始: 这是一段嵌入内容 示例结束。 """ .indent(4 );
高级用法与最佳实践
1. Text Blocks 用于配置文件(如 YAML)
Text Blocks 非常适合用于表示结构化配置,如 YAML、Properties 等,避免了繁琐的拼接和缩进混乱:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 String yamlConfig = """ server: port: 8080 host: 127.0.0.1 datasource: driver: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/shop username: ${DB_USER} password: ${DB_PASSWORD} logging: level: root: INFO com.example: DEBUG """ ;
2. 测试场景中构造结构化数据
Text Blocks 在编写测试时,可以快速构造清晰的 JSON 或 XML,提升可读性和可维护性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Test public void shouldParseUserListCorrectly () { String testJson = """ { "users": [ { "id": 101, "name": "张三", "email": "zhangsan@example.com", "enabled": true }, { "id": 102, "name": "李四", "email": "lisi@example.com", "enabled": false } ] } """ ; JsonObject obj = JsonParser.parseString(testJson).getAsJsonObject(); }
3. 正则表达式模式
当用 Text Blocks 写正则表达式时,要注意结尾的换行可能影响匹配效果。对于单行模式,建议使用 .strip()
清除末尾换行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public class RegexToolkit { private static final String PHONE_REGEX = """ ^1[3-9]\\d{9}$ """ .strip(); private static final String LOG_PATTERN = """ ^(?<timestamp>\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2})\\s+ (?<level>INFO|WARN|ERROR)\\s+ (?<message>.*)$ """ .strip(); private static final String DATE_REGEX = """ (?x) # 开启注释模式 ^ # 行首 (?<year>\\d{4}) # 年 - # 分隔符 (?<month>\\d{2}) # 月 - # 分隔符 (?<day>\\d{2}) # 日 $ # 行尾 """ ; public static void compareStripEffect () { String raw = """ ^hello$ """ ; String cleaned = raw.strip(); System.out.println("原始长度:" + raw.length()); System.out.println("strip 后长度:" + cleaned.length()); System.out.println("未 strip 匹配:" + Pattern.compile(raw).matcher("hello" ).matches()); System.out.println("已 strip 匹配:" + Pattern.compile(cleaned).matcher("hello" ).matches()); } }
什么时候该用 .strip()
?
建议使用 .strip()
的场景:
构建 单行正则 时,为了避免尾部换行影响匹配
需要精确控制字符串的场景(如配置 key、命令行片段)
什么时候不需要 .strip()
?
正则开启了宽松模式 (?x)
,会忽略空格和换行
正则本身就是多行模式,结构依赖换行存在
注意事项:
1 2 3 4 5 6 7 8 9 10 11 12 13 String badRegex = """ ^\\d{6}$ """ ;Pattern.compile(badRegex).matcher("123456" ).matches(); String goodRegex = """ ^\\d{6}$ """ .strip();Pattern.compile(goodRegex).matcher("123456" ).matches();
正则场景用 Text Blocks 要特别小心尾部空白,.strip()
是常用“补丁”。
性能考虑
Text Blocks 在编译阶段就会被转换为普通的 String
实例,因此在运行时几乎没有任何性能损耗 。不过,使用时还是有些细节值得注意:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private static final String SQL_TEMPLATE = """ SELECT id, name, price FROM products WHERE category = ? AND status = ? """ ;public String buildQuery (String category, String status) { return """ SELECT id, name, price FROM products WHERE category = '%s' AND status = '%s' """ .formatted(category, status); } public String buildQuery (String category, String status) { return SQL_TEMPLATE.formatted(category, status); }
为了提高性能,这里建议把模板定义为常量,避免在热点方法中频繁创建相同字符串,尤其是数据库查询、日志格式、消息内容等。
Text Blocks ≠ 原始字符串字面量
有些人误以为 Text Blocks 是“原始字符串”,但其实并不是。Text Blocks 仍然会解析转义字符 ,和真正意义上的“原样输出”并不一样。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 String 示例1 = """ 第一行\n 第二行\t缩进 """ ; String 路径 = """ D:\\Work\\Docs\\report.pdf """ ;String json输出 = """ { "name": "小王", "status": "已完成", "备注": "文件位于 \"D:\\\\Files\"" } """ ;
总结
Text Blocks 是 Java 在多行字符串处理方面的一次重大升级。它不仅让代码更整洁、易读、更少出错 ,更在日常开发中节省了大量维护和格式调整的时间。
为什么推荐使用它呢?主要是基于以下几个优点来考虑的:
少写一堆反斜杠 :写 JSON、HTML 不再满屏 \"
和 \n
。
结构一目了然 :特别适合写 SQL、配置、模板类的内容,层次分明。
维护调试更轻松 :复制粘贴就能直接跑,不容易出错,改起来也省心。
格式化更自然 :配合 .formatted()
使用,方便构建带参数的文本块。
不过使用时要注意以下几点:
搞清楚 开始和结束的定界符规则 ,别被格式坑了。
缩进会自动处理 ,它不是原样输出,这点需要留意。
合理用 .strip()
和 .indent()
,能避免多余的空格或换行带来的小麻烦。
尽量不要在 Text Blocks 里拼字符串 ,保持内容独立更好管理。
还在用旧版 Java?是时候升级了。
你们团队还没人用文本块?这篇文章值得收藏,试过就回不去了。
别再用加号一条条拼字符串了,换种写法,代码清爽,维护也轻松。
想了解更多实用技巧,欢迎关注「Java架构杂谈」。我们下期见~