1. 概述
本文将深入探讨Java中强大的字符串格式化工具——java.util.Formatter
类。这个类提供了灵活的布局对齐和格式化支持,让你能像C语言中的printf
一样优雅地处理字符串格式化需求。
2. 如何使用Formatter
还记得C语言的printf
吗?Java的字符串格式化与之非常相似。Formatter
的核心功能通过String
类的静态方法暴露出来。这个方法接收一个模板字符串和一组参数,用参数填充模板:
String greetings = String.format(
"Hello Folks, welcome to %s !",
"Baeldung");
输出结果为:
"Hello Folks, welcome to Baeldung !"
模板字符串包含静态文本和格式说明符,后者指示参数在特定位置的插入方式。在这个例子中,%s
就是格式说明符,它会被对应的参数替换。
3. 格式说明符详解
3.1. 通用语法
对于常规、字符和数值类型,格式说明符的语法结构如下:
%[argument_index$][flags][width][.precision]conversion
其中:
argument_index
(可选):整数i
,表示使用第i
个参数flags
(可选):修改输出格式的字符集width
(可选):最小输出字符数precision
(可选):限制字符数的整数(具体行为取决于转换类型)conversion
(必需):指示参数如何格式化的字符
✅ 关键点:conversion
是唯一必需部分,其他都是可选修饰符。
如果想显式指定参数位置,可以使用1$
、2$
等索引:
String greetings = String.format(
"Hello %2$s, welcome to %1$s !",
"Baeldung",
"Folks");
3.2. 日期/时间表示
日期/时间格式说明符语法:
%[argument_index$][flags][width]conversion
⚠️ 注意:日期格式必须以t
或T
开头,后跟特定的时间单位字符。
示例:
@Test
public void whenFormatSpecifierForCalendar_thenGotExpected() {
Calendar c = new GregorianCalendar(2017, 11, 10);
String s = String.format(
"The date is: %tm %1$te,%1$tY", c);
assertEquals("The date is: 12 10,2017", s);
}
这里:
%tm
:两位数月份%te
:月份中的日期%tY
:四位数年份
3.3. 无参数格式说明符
语法:
%[flags][width]conversion
目前仅支持两种转换:
%%
:输出百分号%
%n
:输出平台特定的换行符
示例:
@Test
public void whenNoArguments_thenExpected() {
String s = String.format("John scored 90%% in Fall semester");
assertEquals("John scored 90% in Fall semester", s);
}
4. 转换类型详解
4.1. 通用转换
适用于任何参数类型:
b
/B
:布尔值(null
转为false
)h
/H
:哈希码s
/S
:字符串(null
转为"null"
,否则调用toString()
)
示例:
@Test
public void givenString_whenGeneralConversion_thenConvertedString() {
String s = String.format("The correct answer is %s", false);
assertEquals("The correct answer is false", s);
s = String.format("The correct answer is %b", null);
assertEquals("The correct answer is false", s);
s = String.format("The correct answer is %B", true);
assertEquals("The correct answer is TRUE", s);
}
4.2. 字符转换
适用于Unicode字符类型:char
, Character
, byte
, Byte
, short
, Short
,以及有效的int
/Integer
。
语法:c
(小写)或C
(大写)
示例:
@Test
public void givenString_whenCharConversion_thenConvertedString() {
String s = String.format("The correct answer is %c", 'a');
assertEquals("The correct answer is a", s);
s = String.format("The correct answer is %c", null);
assertEquals("The correct answer is null", s);
s = String.format("The correct answer is %C", 'b');
assertEquals("The correct answer is B", s);
s = String.format("The valid unicode character: %c", 0x0400);
assertTrue(Character.isValidCodePoint(0x0400));
assertEquals("The valid unicode character: Ѐ", s);
}
❌ 踩坑:无效码点会抛出IllegalFormatCodePointException
:
@Test(expected = IllegalFormatCodePointException.class)
public void whenIllegalCodePointForConversion_thenError() {
String s = String.format("The valid unicode character: %c", 0x11FFFF);
}
4.3. 数值 - 整数类型
适用于Java整数类型:byte
, Byte
, short
, Short
, int
, Integer
, long
, Long
, BigInteger
转换类型:
d
:十进制o
:八进制x
/X
:十六进制
示例:
@Test
public void whenNumericIntegralConversion_thenConvertedString() {
String s = String.format("The number 25 in decimal = %d", 25);
assertEquals("The number 25 in decimal = 25", s);
s = String.format("The number 25 in octal = %o", 25);
assertEquals("The number 25 in octal = 31", s);
s = String.format("The number 25 in hexadecimal = %x", 25);
assertEquals("The number 25 in hexadecimal = 19", s);
}
4.4. 数值 - 浮点类型
适用于Java浮点类型:float
, Float
, double
, Double
, BigDecimal
转换类型:
e
/E
:科学计数法f
:十进制数g
/G
:根据精度自动选择科学计数法或十进制
示例:
@Test
public void whenNumericFloatingConversion_thenConvertedString() {
String s = String.format(
"The computerized scientific format of 10000.00 "
+ "= %e", 10000.00);
assertEquals(
"The computerized scientific format of 10000.00 = 1.000000e+04", s);
String s2 = String.format("The decimal format of 10.019 = %f", 10.019);
assertEquals("The decimal format of 10.019 = 10.019000", s2);
}
4.5. 其他转换
- 日期/时间:需使用
t
/T
前缀(如%tY
) - 百分号:
%%
输出%
- 行分隔符:
%n
输出平台换行符
示例:
@Test
public void whenLineSeparatorConversion_thenConvertedString() {
String s = String.format("First Line %nSecond Line");
assertEquals("First Line \n" + "Second Line", s);
}
5. 标志位
标志位用于修改输出格式。常用标志包括:
-
:左对齐0
:零填充+
:包含正负号,
:包含千位分隔符
示例(左对齐标志):
@Test
public void whenSpecifyFlag_thenGotFormattedString() {
String s = String.format("Without left justified flag: %5d", 25);
assertEquals("Without left justified flag: 25", s);
s = String.format("With left justified flag: %-5d", 25);
assertEquals("With left justified flag: 25 ", s);
}
6. 精度控制
精度在不同转换类型中有不同含义:
- 通用转换:最大输出字符数
- 浮点转换:小数点后位数
示例:
@Test
public void whenSpecifyPrecision_thenGotExpected() {
String s = String.format(
"Output of 25.09878 with Precision 2: %.2f", 25.09878);
assertEquals("Output of 25.09878 with Precision 2: 25.10", s);
String s2 = String.format(
"Output of general conversion type with Precision 2: %.2b", true);
assertEquals("Output of general conversion type with Precision 2: tr", s2);
}
7. 参数索引
参数索引指定使用哪个参数:
1$
:第一个参数2$
:第二个参数<
:复用前一个参数
示例:
@Test
public void whenSpecifyArgumentIndex_thenGotExpected() {
Calendar c = Calendar.getInstance();
String s = String.format("The date is: %tm %1$te,%1$tY", c);
assertEquals("The date is: 12 10,2017", s);
s = String.format("The date is: %tm %<te,%<tY", c);
assertEquals("The date is: 12 10,2017", s);
}
✅ 技巧:使用<
可以避免重复写索引,代码更简洁。
8. 其他使用方式
8.1. 与Appendable配合使用
直接创建Formatter
实例并绑定到Appendable
对象(如StringBuilder
):
@Test
public void whenCreateFormatter_thenFormatterWithAppendable() {
StringBuilder sb = new StringBuilder();
Formatter formatter = new Formatter(sb);
formatter.format("I am writting to a %s Instance.", sb.getClass());
assertEquals(
"I am writting to a class java.lang.StringBuilder Instance.",
sb.toString());
}
类似地,可以绑定到OutputStream
、File
等目标。
9. 总结
java.util.Formatter
提供了强大而灵活的字符串格式化能力。通过组合使用:
- 格式说明符语法
- 多种转换类型
- 精度控制
- 标志位修饰
- 参数索引引用
我们可以实现从简单文本替换到复杂日期/时间格式化的各种需求。简单粗暴地说:掌握这个工具,Java字符串格式化从此无忧。
本文示例代码可在GitHub仓库获取。