兄弟们,鹏磊今天来聊聊 Java 22 里的字符串模板(String Templates)这个特性,说实话,这玩意儿挺实用的,能让你字符串拼接更简单,代码也更清晰。
字符串模板是 Java 22 的第二次预览功能(JEP 459),允许在字符串中嵌入表达式,运行时替换这些表达式。以前写字符串拼接,得用 + 号或者 StringBuilder,现在直接用模板,一行搞定,而且更安全,能防止注入攻击。
基本语法
字符串模板的语法很简单,用模板处理器(比如 STR)加上点号 . 和模板字符串,表达式用 \{表达式} 包裹:
// 基本用法,用 STR 处理器
String name = "张三"; // 定义名字变量
int age = 25; // 定义年龄变量
String info = STR."用户 \{name} 的年龄是 \{age}"; // 用 STR 处理器处理模板,嵌入表达式
System.out.println(info); // 输出:用户 张三 的年龄是 25
这玩意儿确实方便,不用再写那些啰嗦的字符串拼接了。表达式可以是任何 Java 表达式,变量、方法调用、计算表达式都行。
内置模板处理器
Java 22 提供了几个内置的模板处理器,最常用的是 STR 和 FMT:
STR 处理器
STR 是基本的字符串插值处理器,直接把表达式的值插入到字符串里:
// STR 处理器基本用法
String name = "李四"; // 名字
int score = 95; // 分数
String message = STR."学生 \{name} 的分数是 \{score}"; // 用 STR 处理模板
System.out.println(message); // 输出:学生 李四 的分数是 95
// 表达式可以是方法调用
String time = STR."当前时间: \{java.time.LocalDateTime.now()}"; // 调用方法获取当前时间
System.out.println(time); // 输出当前时间
// 表达式可以是计算
int a = 10; // 第一个数
int b = 20; // 第二个数
String result = STR."\{a} + \{b} = \{a + b}"; // 计算表达式
System.out.println(result); // 输出:10 + 20 = 30
STR 处理器会把表达式的值转成字符串,然后插入到模板里,简单直接。
FMT 处理器
FMT 是格式化处理器,支持格式化字符串,类似 String.format():
// FMT 处理器,支持格式化
double price = 99.99; // 价格
String formatted = FMT."价格: %.2f 元"; // 格式化价格,保留两位小数
System.out.println(formatted); // 输出:价格: 99.99 元
// 多个格式化参数
int count = 5; // 数量
String info = FMT."商品数量: %d, 单价: %.2f"; // 格式化数量和价格
System.out.println(info); // 输出:商品数量: 5, 单价: 99.99
FMT 处理器支持 String.format() 的所有格式化选项,用起来更方便。
多行模板
字符串模板支持多行,类似文本块(text block)的语法:
// 多行模板,用三引号
String multiLine = STR."""
这是第一行
这是第二行
变量值: \{name}
"""; // 多行模板,可以跨越多行
System.out.println(multiLine); // 输出多行内容
多行模板会自动处理缩进,保留格式,写起来更方便。
记录和对象模板
字符串模板可以处理记录(Record)和对象,提取字段值:
// 定义记录
record Person(String name, int age, String city) {} // 人员记录,包含姓名、年龄、城市
// 创建记录实例
var person = new Person("王五", 30, "上海"); // 创建人员对象
// 在模板中使用记录
String info = STR."姓名: \{person.name()}, 年龄: \{person.age()}, 城市: \{person.city()}"; // 提取记录字段
System.out.println(info); // 输出:姓名: 王五, 年龄: 30, 城市: 上海
这玩意儿在处理对象的时候特别有用,可以直接提取字段值,不用手动拼接。
JSON 模板
字符串模板还可以用来生成 JSON,虽然 Java 22 没有内置 JSON 处理器,但可以自己实现或者用第三方库:
// 生成 JSON 字符串(需要自定义 JSON 处理器或第三方库)
record User(String name, int age, String email) {} // 用户记录
var user = new User("赵六", 25, "[email protected]"); // 创建用户对象
// 手动构建 JSON(实际项目中建议用 JSON 处理器)
String json = STR."""
{
"name": "\{user.name()}",
"age": \{user.age()},
"email": "\{user.email()}"
}
"""; // 手动构建 JSON 字符串
System.out.println(json); // 输出 JSON 格式
虽然 Java 22 没有内置 JSON 处理器,但可以自己实现一个,或者等后续版本。
安全性
字符串模板的设计考虑了安全性,能防止注入攻击:
// SQL 注入防护(需要自定义 SQL 处理器)
String userInput = "'; DROP TABLE users; --"; // 恶意输入
// 如果用字符串拼接,会有 SQL 注入风险
// String sql = "SELECT * FROM users WHERE name = '" + userInput + "'"; // 危险!
// 用字符串模板处理器,可以转义特殊字符
// String sql = SQL."SELECT * FROM users WHERE name = \{userInput}"; // 安全,会自动转义
字符串模板处理器可以自定义处理逻辑,转义特殊字符,防止注入攻击。虽然 Java 22 没有内置 SQL 处理器,但可以自己实现。
自定义模板处理器
可以自定义模板处理器,实现自己的处理逻辑:
// 自定义模板处理器(简化示例)
import java.util.function.Function;
// 简单的自定义处理器
Function<StringTemplate, String> UPPER = st -> {
StringBuilder sb = new StringBuilder(); // 创建字符串构建器
var fragments = st.fragments(); // 获取模板片段
var values = st.values(); // 获取表达式值
for (int i = 0; i < fragments.size(); i++) { // 遍历片段
sb.append(fragments.get(i)); // 添加片段
if (i < values.size()) { // 如果有值
sb.append(values.get(i).toString().toUpperCase()); // 转大写后添加
}
}
return sb.toString(); // 返回结果
};
// 使用自定义处理器
String name = "test";
String result = UPPER.apply(STR."Hello \{name}"); // 使用自定义处理器
System.out.println(result); // 输出:Hello TEST
自定义处理器可以实现各种功能,比如转义、格式化、验证等等。
实际应用场景
字符串模板在实际开发中有很多应用场景:
日志记录
// 日志记录,用字符串模板更清晰
String userId = "12345"; // 用户 ID
String action = "登录"; // 操作
String log = STR."用户 \{userId} 执行了 \{action} 操作"; // 构建日志消息
logger.info(log); // 记录日志
错误消息
// 错误消息,用模板更清晰
String field = "用户名"; // 字段名
String error = STR."\{field} 不能为空"; // 构建错误消息
throw new IllegalArgumentException(error); // 抛出异常
配置构建
// 构建配置字符串
String host = "localhost"; // 主机
int port = 8080; // 端口
String config = STR."http://\{host}:\{port}/api"; // 构建配置 URL
System.out.println(config); // 输出:http://localhost:8080/api
消息模板
// 消息模板
String recipient = "张三"; // 收件人
String message = STR."""
亲爱的 \{recipient}:
这是一条测试消息。
祝好!
"""; // 多行消息模板
System.out.println(message); // 输出消息
性能考虑
字符串模板的性能和 StringBuilder 差不多,编译后会优化成高效的代码。不过因为是预览功能,性能可能还有优化空间。
// 性能对比(简化示例)
// 字符串拼接
String s1 = "Hello " + name + ", age: " + age; // 传统方式
// 字符串模板
String s2 = STR."Hello \{name}, age: \{age}"; // 模板方式,性能差不多
实际使用中,性能差异不大,主要看代码可读性。
注意事项和限制
字符串模板虽然好用,但也有一些限制:
预览功能
字符串模板在 Java 22 中是预览功能,API 可能会变,生产环境慎用。不过可以先用起来,熟悉一下,等正式版出来再迁移。
需要启用预览功能
编译和运行的时候需要加 --enable-preview 参数:
# 编译
javac --enable-preview --release 22 Main.java
# 运行
java --enable-preview Main
表达式必须是有效的 Java 表达式
// 正确
String s = STR."Value: \{value}"; // 有效的表达式
// 错误
// String s = STR."Value: \{value +}"; // 语法错误
模板处理器必须存在
// STR 和 FMT 是内置的,可以直接用
String s = STR."Hello"; // 正确
// 自定义处理器需要先定义
// String s = CUSTOM."Hello"; // 如果 CUSTOM 不存在,会编译错误
迁移建议
如果兄弟们想把旧代码迁移到字符串模板,可以这么做:
识别可以替换的地方
- 字符串拼接(
+号) String.format()调用StringBuilder构建- 消息模板
逐步替换
不要一次性全替换,先从一个文件开始,看看效果,再逐步推广。
注意预览功能
因为是预览功能,API 可能会变,建议先在小项目里试试,等正式版出来再大规模使用。
总结
字符串模板(JEP 459)这个特性,说实话,挺实用的。能让字符串拼接更简单,代码也更清晰,特别是在处理复杂字符串的时候。虽然还是预览功能,但值得试试,特别是 STR 和 FMT 这两个内置处理器,用起来很方便。
兄弟们如果还没用过,可以试试,特别是在日志记录、错误消息、配置构建这些场景,用字符串模板会更清晰。好了,就这些,有啥问题欢迎反馈。