03、Java 22 新特性:字符串模板(JEP 459)实战应用

兄弟们,鹏磊今天来聊聊 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 提供了几个内置的模板处理器,最常用的是 STRFMT

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)这个特性,说实话,挺实用的。能让字符串拼接更简单,代码也更清晰,特别是在处理复杂字符串的时候。虽然还是预览功能,但值得试试,特别是 STRFMT 这两个内置处理器,用起来很方便。

兄弟们如果还没用过,可以试试,特别是在日志记录、错误消息、配置构建这些场景,用字符串模板会更清晰。好了,就这些,有啥问题欢迎反馈。

本文章最后更新于 2025-11-27