2. Java中的字符串组合
在编程中,我们经常需要拼接字符串。Java 提供了多种方法,但每种都有明显的短板。
2.1. 字符串拼接
最基础的方式是使用 +
号拼接字符串字面量和表达式:
String composeUsingPlus(String feelsLike, String temperature, String unit){
return "Today's weather is " + feelsLike +
", with a temperature of " + temperature + " degrees " + unit;
}
虽然功能实现了,但代码可读性差(全是加号),维护起来简直是噩梦。
2.2. StringBuilder/StringBuffer
使用 Java 内置的 StringBuilder
或 StringBuffer
类,通过 append()
方法拼接:
String composeUsingStringBuilder(String feelsLike, String temperature, String unit) {
return new StringBuilder()
.append("Today's weather is ")
.append(feelsLike)
.append(", with a temperature of ")
.append(temperature)
.append(" degrees ")
.append(unit)
.toString();
}
虽然内存效率高,但遵循 Builder 模式的写法太啰嗦,代码量爆炸。
2.3. 格式化字符串
通过 String.format()
或 formatted()
分离静态模板和动态参数:
String composeUsingFormatters(String feelsLike, String temperature, String unit) {
return String.format("Today's weather is %s, with a temperature of %s degrees %s",
feelsLike, temperature, unit);
}
模板固定了,但参数顺序和数量必须严格匹配,稍有不慎就翻车。
2.4. MessageFormat 类
java.text.MessageFormat
支持带占位符的国际化文本拼接:
String composeUsingMessageFormatter(String feelsLike, String temperature, String unit) {
return MessageFormat.format("Today''s weather is {0}, with a temperature of {1} degrees {2}",
feelsLike, temperature, unit);
}
同样存在参数顺序问题,而且语法和常规字符串写法差异太大,用起来别扭。
3. 字符串模板入门
传统方法各有硬伤,字符串模板(String Templates)作为 Java 21 的预览特性(JEP 430)给出了解决方案。
3.1. 设计目标
引入字符串模板的核心目标:
- 简化运行时拼接字符串的表达方式
- 提升可读性,告别
StringBuilder
的冗长代码 - 在安全性和便利性之间找平衡,避免其他语言中字符串插值的安全漏洞
- 允许第三方库自定义字符串格式化语法
3.2. 模板表达式
核心概念是模板表达式——一种新型可编程表达式:
- ✅ 不仅能做字符串插值
- ✅ 保证组合过程的安全与高效
- ✅ 输出结果不限于字符串(可转为任意对象)
模板表达式由三部分组成:
- 处理器(Processor)
- 包含嵌入表达式的模板
- 点号(
.
)分隔符
4. 模板处理器
处理器负责在运行时计算嵌入表达式,将其与字符串字面量合并生成最终结果。 Java 提供内置处理器,也支持自定义处理器。
⚠️ 这是 Java 21 的预览特性,需启用 --enable-preview
。
4.1. STR 处理器
STR
是最常用的内置处理器,通过将嵌入表达式替换为字符串化值实现插值:
String interpolationUsingSTRProcessor(String feelsLike, String temperature, String unit) {
return STR
. "Today's weather is \{ feelsLike }, with a temperature of \{ temperature } degrees \{ unit }" ;
}
STR
是自动导入的公共静态常量字段。
支持多行文本块(用 """
包裹):
String interpolationOfJSONBlock(String feelsLike, String temperature, String unit) {
return STR
. """
{
"feelsLike": "\{ feelsLike }",
"temperature": "\{ temperature }",
"unit": "\{ unit }"
}
""" ;
}
还能直接嵌入运行时计算的表达式:
String interpolationWithExpressions() {
return STR
. "Today's weather is \{ getFeelsLike() }, with a temperature of \{ getTemperature() } degrees \{ getUnit() }";
}
4.2. FMT 处理器
FMT
处理器支持格式化指令(类似 java.util.Formatter
):
String interpolationOfJSONBlockWithFMT(String feelsLike, float temperature, String unit) {
return FMT
. """
{
"feelsLike": "%1s\{ feelsLike }",
"temperature": "%2.2f\{ temperature }",
"unit": "%1s\{ unit }"
}
""" ;
}
通过 %s
和 %f
等指令控制输出格式,比如保留两位小数。
4.3. 模板表达式求值过程
以这段代码为例:
STR
. "Today's weather is \{ feelsLike }, with a temperature of \{ temperature } degrees \{ unit }" ;
实际执行分三步:
- 获取处理器实例(这里是
STR
) - 创建未处理的模板对象(通过
RAW
处理器):StringTemplate str = RAW . "Today's weather is \{ getFeelsLike() }, with a temperature of \{ getTemperature() } degrees \{ getUnit() }" ;
- 调用处理器的
process()
方法:return STR.process(str);
5. 字符串插值与模板的关系
虽然模板表达式看起来像字符串插值,但安全性是其核心优势:
- ❌ 禁止直接将带嵌入表达式的字符串字面量转为输出字符串
- ✅ 处理器必须验证插值的安全性和正确性
- ❌ 缺少处理器会触发编译错误
- ❌ 处理器插值失败会抛出异常
Java 根据嵌入表达式是否存在来区分:
-
"text"
→StringLiteral
(无嵌入表达式) -
"text \{expr}"
→StringTemplate
(有嵌入表达式) -
"""text"""
→TextBlock
-
"""text \{expr}"""
→TextBlockTemplate
关键区别: 模板是 java.lang.StringTemplate
接口类型,不是 java.lang.String
。
6. 总结
我们对比了传统字符串组合方式的缺陷,深入解析了 Java 21 中字符串模板的设计哲学。通过 STR
/FMT
处理器和模板表达式机制,Java 在保持类型安全的前提下,提供了简洁高效的字符串拼接方案,比直接字符串插值更可靠。
示例代码已上传至 GitHub