前言

本文详细描述了字符串格式化的语法与使用案例。

版本约定

字符串格式化包含两个部分,格式字符串和参数列表。格式字符串是一个 String 串,它可以包含固定文本以及一个或多个嵌入的格式说明符。参数列表是一系列需要填充到格式字符串中的参数值。

官方文档参考:https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html

官方文档看着人晕乎晕乎的,我结合网上一些文章,稍作整理,以更加便于理解的布局方式呈现。文中如果存在歧义的地方,你也可以参考官方文档。

在 Java 的 String 类中,提供了 format() 方法格式化字符串,该方法有两种重载形式。

  1. String.format(String format, Object... args)
  2. String.format(Locale locale, String format, Object... args)

两者的唯一区别是前者使用本地语言环境,后者使用指定语言环境。

语法

  1. 常规,字符和数字类型的格式说明符具有以下语法:
    1. %[argument_index$][flags][width][.precision]conversion
  • arguments_index:可选参数,是一个十进制整数,表示参数在参数列表中的位置。比如,1$ 表示第一个参数,2$ 表示第二个参数,以此类推;
  • flags:可选参数,表示一组修改输出格式的字符,这组 flags 集取决于 conversion;
  • width:可选参数,是一个十进制整数,表示要写入输出的最小字符数;
  • precision:可选参数,是一个十进制整数,是一个非负十进制整数,通常用于限制字符数,具体行为取决于 conversion;
  • conversion:必选参数,是一个字符,表示应该如何格式化参数。
  1. 用于表示日期和时间的类型的格式说明符具有以下语法:
    1. %[argument_index$][flags][width]conversion
  • 可选参数 argument_index,flags,width 的定义同上;
  • conversion:必选参数,是一个由两个字符组成的序列,第一个字符是 ‘t’ 或 ‘T’,第二个字符表示要使用的格式,这些字符与 GNU date 和 POSIX strftime(3c)定义的字符相似但不完全相同。
  1. 与参数不对应的格式说明符具有以下语法(不知道啥意思):
    1. %[flags][width]conversion
  • 可选参数 flags,width 的定义同上;
  • conversion:必选参数,是一个字符,该字符表示要在输出中插入的内容。

格式说明符的语法图:
image.png
看完上面的语法,基本也不知道说的是啥,接下来我们结合案例来理解。

另外,我在看《Java 核心技术 卷1》的时候发现,System.out.printf(String format, Object ... args) 方法也支持与字符串格式化一样的语法。

我对比了一下两者的源码,发现都是用的 Formatter 类实现的格式化,所以两者的格式化语法是一样的。

  1. // String.format
  2. public static String format(String format, Object... args) {
  3. return new Formatter().format(format, args).toString();
  4. }
  5. // System.out.printf
  6. public PrintStream printf(String format, Object ... args) {
  7. return format(format, args);
  8. }
  9. public PrintStream format(String format, Object ... args) {
  10. try {
  11. synchronized (this) {
  12. ensureOpen();
  13. if ((formatter == null)
  14. || (formatter.locale() != Locale.getDefault()))
  15. formatter = new Formatter((Appendable) this);
  16. formatter.format(Locale.getDefault(), format, args);
  17. }
  18. } catch (InterruptedIOException x) {
  19. Thread.currentThread().interrupt();
  20. } catch (IOException x) {
  21. trouble = true;
  22. }
  23. return this;
  24. }

这里顺便提一嘴,Formatter 类是线程不安全的,原因和 SimpleDateFormat 类似,在 Formatter 类中有一个实例属性 a,用来存储格式化后的字符串数据。

官方文档中也提示了:
image.png
参考官方文档,我们大致可以字符串格式化分为两大类:常规类型格式化和日期时间类型格式化,接下来我们分别讲解。

常规类型格式化

Conversion

根据语法,我们知道 conversion 是必选参数,不同的 conversion(转换符) 实现不同的数据类型到字符串的转换。

转换可以支持的参数类型如下所示:

  1. General:可以应用于任何参数类型;
  2. Character:可以应用于代表 Unicode 字符的基本类型:char,Character,byte,Byte,short 和 Short,当Character.isValidCodePoint(int) 返回 true 时,这种转换也可以应用于 int 和 Integer 类型。
  3. Numeric
    1. Integral:可以应用于 Java 整数类型:byte,Byte,short,Short,int,Integer,long,Long 和 BigInteger(但不适用 char 和 Character 类型);
    2. Floating Point:可以应用于 Java 浮点类型:float,Float,double,Double 和 BigDecimal;
  4. Date/Time:可以应用于能够对日期或时间进行编码的 Java 类型:long、Long、Calendar、Date 和TemporalAccessor;
  5. Percent:产生一个的 “%”(’\u0025’);
  6. Line Separator:产生平台专用的行分隔符。 | 转换符 | 参数类别 | 说明 | | —- | —- | —- | | ‘b’, ‘B’ | general |
    - 如果参数 arg 为 null,则结果为 false;
    - 如果 arg 是 boolean 或者 Boolean,则会执行 String.valueOf(arg) 方法,返回结果;
    - 否则,结果为 true。
    | | ‘h’, ‘H’ | general |
    - 如果参数 arg 为 null,则结果为 null;
    - 否则,执行 Integer.toHexString(arg.hashCode()) 方法,返回结果。
    | | ‘s’, ‘S’ | general |
    - 如果参数 arg 为 null,则结果为 null;
    - 如果 arg 实现了 Formattable 接口,则会执行 arg.formatTo() 方法,返回结果;
    - 否则,执行 arg.toString() 方法,返回结果。
    | | ‘c’, ‘C’ | character | 结果是一个 Unicode 字符。 | | ‘d’ | integral | 结果格式化为十进制的整数。 | | ‘o’ | integral | 结果格式化为八进制的整数。 | | ‘x’, ‘X’ | integral | 结果格式化为十六进制的整数。 | | ‘e’, ‘E’ | floating point | 结果以计算机科学计数形式格式化为十进制数。 | | ‘f’ | floating point | 结果格式化为十进制数字。 | | ‘g’, ‘G’ | floating point | 根据精度和舍入后的值,使用计算机科学计数法或十进制格式对结果进行格式化。 | | ‘a’, ‘A’ | floating point | 其结果被格式化为一个十六进制的浮点数,带有一个显数和一个指数。BigDecimal 类型不支持这种转换,尽管后者属于浮点参数类别。 | | ‘t’, ‘T’ | date/time | 日期和时间转换字符的前缀。 | | ‘%’ | percent | 结果是文字 ‘%’。 | | ‘n’ | line separator | 结果是特定于平台的行分隔符。 |

搭配例子来理解上面的语法内容,这里不会覆盖所有的转换符,只介绍几个常用的。有特殊需求的,大家结合上面的语法,自己实现例子,在使用的时候,多写写 Unit Test,可以避免很多错误。

比如转换符 ‘b’, ‘B’,一般是用来处理 Boolean 类型的,测试几个例子,看看是否和上面的语法说明能对应上。

  1. public static void main(String[] args) {
  2. System.out.println(String.format("转换符是 b,参数是 null,格式化结果:%b", null));
  3. System.out.println(String.format("转换符是 b,参数是 false,格式化结果:%b", false));
  4. System.out.println(String.format("转换符是 b,参数是 new Boolean(true),格式化结果:%b", new Boolean(true)));
  5. System.out.println(String.format("转换符是 b,参数是空字符串,格式化结果:%b", ""));
  6. System.out.println(String.format("转换符是 b,参数是 1,格式化结果:%b", 1));
  7. }

运行程序,输出:

  1. 转换符是 b,参数是 null,格式化结果:false
  2. 转换符是 b,参数是 false,格式化结果:false
  3. 转换符是 b,参数是 new Boolean(true),格式化结果:true
  4. 转换符是 b,参数是空字符串,格式化结果:true
  5. 转换符是 b,参数是 1,格式化结果:true

确实符合上面的语法描述,特别是最后一条,对于其他类型的参数,格式化后的结果都是 true。

接下来,我们测一下转换符 ‘s’, ‘S’,这个转换符一般用来处理字符串类型,同样的对应上面的语法说明,我们测试几个例子。因为我这里没有实现了 Formattable 接口的类,所以这个就不测试了。

  1. public static void main(String[] args) {
  2. System.out.println(String.format("转换符是 s,参数是 null,格式化结果:%s", null));
  3. System.out.println(String.format("转换符是 s,参数是 \"测试\",格式化结果:%s", "测试"));
  4. }

运行程序,输出:

  1. 转换符是 s,参数是 null,格式化结果:null
  2. 转换符是 s,参数是 "测试",格式化结果:测试

其他的转换符,大家有需要的,在使用前,自己写几个例子测试一下。

Argument Index

这个比较好理解,表示参数在参数列表中的位置。参教索引值从 1 开始,而不是从 0 开始,比如,1$ 表示第一个参数,2$ 表示第二个参数,以此类推,举个例子。

  1. public static void main(String[] args) {
  2. Calendar c = Calendar.getInstance();
  3. String s = String.format("张三的生日是:%1$tY-%1$tm-%1$te", c);
  4. System.out.println(s);
  5. }

运行程序,输出:

  1. 张三的生日是:2021-05-25

这里格式化字符串中取的都是第一个参数。

需要注意的是,一旦我们在格式化字符串中使用了 argument_index,则所有占位符都要加上。

Flags

flags 也是可选参数,用于控制输出的格式,比如左对齐、金额用逗号隔开等。

下表总结了所支持的标志,y 表示该标志支持的参数类型。

flags General Character Integral Floating Point Date/Time 描述
‘-‘ y y y y y 向左对齐。
‘#’ y1 - y3 y -
- 如果是 16 进制则在数字前面添加 0x;
- 如果是 8 进制则在数字前面添加 0;
- 有没有其他特性目前不知道。
‘+’ - - y4 y - 为正数或者负数添加符号。
‘ ‘ - - y4 y - 在数字前面添加指定数量的空格。
‘0’ - - y y - 在数字前面添加指定数量的 0。
‘,’ - - y2 y5 - 每 3 位数字之间用 , 分隔。
‘(‘ - - y4 y5 - 若参数是负数,则结果中不添加负号而是用圆括号把数字括起来。

1:取决于 Formattable 的定义;
2:仅用于 ‘d’ 转换;
3:仅用于 ‘o’,’ x’ 和 ‘X’ 转换;
4:适用于 BigInteger 的 ‘d’,’o’,’x’ 和 ‘X’ 转换,或适用于 byte,Byte,short,Short,Int,Integer,long 和 Long 的 ‘d’ 转换;
5:仅适用于 ‘e’,’E’,’f’,’g’ 和 ‘G’ 的转换。

结合例子来理解,当然下面的例子没有覆盖所有的情况,所以如果有同学有更好的案例,更好的使用场景,可以告诉我,我一并整理进来,特别是我对 ‘#’ 这个 flags 不是特别理解,官网描述的也不清晰。

  1. public static void main(String[] args) {
  2. System.out.println(String.format("flags 是 -,参数是 1,格式化结果:%-8d.", 1));
  3. System.out.println(String.format("flags 是 #,参数是 99,格式化结果:%#x.", 99));
  4. System.out.println(String.format("flags 是 #,参数是 99,格式化结果:%#o.", 99));
  5. System.out.println(String.format("flags 没有,参数是 10.0,格式化结果:%f.", 10.0));
  6. System.out.println(String.format("flags 是 #,参数是 10.0,格式化结果:%#f.", 10.0));
  7. System.out.println(String.format("flags 没有,参数是 10.1234564,格式化结果:%f.", 10.1234564));
  8. System.out.println(String.format("flags 是 #,参数是 10.1234564,格式化结果:%#f.", 10.1234564));
  9. System.out.println(String.format("flags 没有,参数是 10.1234565,格式化结果:%f.", 10.1234565));
  10. System.out.println(String.format("flags 是 #,参数是 10.1234565,格式化结果:%#f.", 10.1234565));
  11. System.out.println(String.format("flags 是 +,参数是 10,格式化结果:%+d.", 10));
  12. System.out.println(String.format("flags 是 +,参数是 -10,格式化结果:%+d.", -10));
  13. System.out.println(String.format("flags 是 +,参数是 10,格式化结果:% 6d.", 10));
  14. System.out.println(String.format("flags 是 +,参数是 -10,格式化结果:% 6d.", -10));
  15. System.out.println(String.format("flags 是 +,参数是 10,格式化结果:%06d.", 10));
  16. System.out.println(String.format("flags 是 +,参数是 -10,格式化结果:%06d.", -10));
  17. System.out.println(String.format("flags 是 +,参数是 9999999,格式化结果:%,6d.", 9999999));
  18. System.out.println(String.format("flags 是 +,参数是 -9999999,格式化结果:%,6d.", -9999999));
  19. System.out.println(String.format("flags 是 +,参数是 -9999999.1234567,格式化结果:%,6f.", -9999999.1234567));
  20. System.out.println(String.format("flags 是 (,参数是 -10,格式化结果:%(d.", -10));
  21. }

运行程序,输出:

  1. flags -,参数是 1,格式化结果:1 .
  2. flags #,参数是 99,格式化结果:0x63.
  3. flags #,参数是 99,格式化结果:0143.
  4. flags 没有,参数是 10.0,格式化结果:10.000000.
  5. flags #,参数是 10.0,格式化结果:10.000000.
  6. flags 没有,参数是 10.1234564,格式化结果:10.123456.
  7. flags #,参数是 10.1234564,格式化结果:10.123456.
  8. flags 没有,参数是 10.1234565,格式化结果:10.123457.
  9. flags #,参数是 10.1234565,格式化结果:10.123457.
  10. flags +,参数是 10,格式化结果:+10.
  11. flags +,参数是 -10,格式化结果:-10.
  12. flags +,参数是 10,格式化结果: 10.
  13. flags +,参数是 -10,格式化结果: -10.
  14. flags +,参数是 10,格式化结果:000010.
  15. flags +,参数是 -10,格式化结果:-00010.
  16. flags +,参数是 9999999,格式化结果:9,999,999.
  17. flags +,参数是 -9999999,格式化结果:-9,999,999.
  18. flags +,参数是 -9999999.1234567,格式化结果:-9,999,999.123457.
  19. flags (,参数是 -10,格式化结果:(10).

Width

也是可选参数,关于这个参数,上面的例子中也有用到,就是用于控制输出的宽度。

  1. public static void main(String[] args) {
  2. System.out.println(String.format("width 是 6,不满 6 位用 0 填充,参数是 10,格式化结果:%06d.", 10));
  3. }

运行程序,输出:

  1. width 6,不满 6 位用 0 填充,参数是 10,格式化结果:000010.

需要注意的是 width 不能设置为 0,否则可能会报错。比如上面的例子,我们把 width 设置为 0:

  1. public static void main(String[] args) {
  2. System.out.println(String.format("width 是 0,不满 6 位用 0 填充,参数是 10,格式化结果:%00d.", 10));
  3. }

运行程序,输出:

  1. Exception in thread "main" java.util.DuplicateFormatFlagsException: Flags = '0'
  2. at java.util.Formatter$Flags.parse(Formatter.java:4443)
  3. at java.util.Formatter$FormatSpecifier.flags(Formatter.java:2640)
  4. at java.util.Formatter$FormatSpecifier.<init>(Formatter.java:2709)
  5. at java.util.Formatter.parse(Formatter.java:2560)
  6. at java.util.Formatter.format(Formatter.java:2501)
  7. at java.util.Formatter.format(Formatter.java:2455)
  8. at java.lang.String.format(String.java:2940)
  9. at Test9.Test5.main(Test5.java:10)

Precision

也是可选参数,用来限定输出的精度,用于格式化浮点数类型。

  1. public static void main(String[] args) {
  2. System.out.println(String.format("precision 是 2,设置精度为 2,参数是 9.1234,格式化结果:%.2f.", 9.1234));
  3. }

运行程序,输出:

  1. precision 2,设置精度为 2,参数是 9.1234,格式化结果:9.12.

需要注意的是不能对整型类型参数设置精度,否则会抛出异常。

  1. public static void main(String[] args) {
  2. System.out.println(String.format("precision 是 2,设置精度为 2,参数是 10,格式化结果:%.2d.", 10));
  3. }

运行程序,输出:

  1. Exception in thread "main" java.util.IllegalFormatPrecisionException: 2
  2. at java.util.Formatter$FormatSpecifier.checkInteger(Formatter.java:2984)
  3. at java.util.Formatter$FormatSpecifier.<init>(Formatter.java:2729)
  4. at java.util.Formatter.parse(Formatter.java:2560)
  5. at java.util.Formatter.format(Formatter.java:2501)
  6. at java.util.Formatter.format(Formatter.java:2455)
  7. at java.lang.String.format(String.java:2940)
  8. at Test9.Test5.main(Test5.java:10)

日期时间类型格式化

String.format() 方法提供了很多关于日期时间类型的格式化,极大的方便了日常的开发。

在语法部分,我们知道日期时间格式化相比常规类型格式化少了 precision(精度),同时 conversion 是由两个字符组成,且第一个字符固定为 ‘t’ 或 ‘T’。

那么第二个字符,日期时间格式化的转换符可以分为如下三类:

  1. 时间格式化转换符;
  2. 日期格式化转换符;
  3. 日期时间格式化转换符。

    时间格式化转换符

下表是用于格式化时间的转换字符:

转换符 说明 示例
H 2 位数字 24 时制的小时,不足 2 位前面补 0。 15
I 2 位数字 12 时制的小时,不足 2 位前面补 0。 03
k 2 位数字 24 时制的小时,前面不补 0。 15
l 2 位数字 12 时制的小时,前面不补 0。 3
M 2 位数字的分钟,不足 2 位前面补 0。 03
S 2 位数字的秒,不足 2 位前面补 0。 09
L 3 位数字的毫秒,不足 3 位前面补 0。 015
N 9 位数字的毫秒,不足 9 位前面补 0。 562000000
p 小写字母的上午或下午标记。 中:下午
英:pm
z 相对于 GMT 的 RFC822 时区的偏移量。 +0800
Z 时区缩写字符串。 CST
s 1970-1-1 00:00:00 到现在所经过的秒数。 1193468128
Q 1970-1-1 00:00:00 到现在所经过的毫秒数。 1193468128984

结合例子来理解上面的语法。

  1. public static void main(String[] args) {
  2. Date date = new Date();
  3. System.out.println(String.format("2 位数字 24 时制的小时,不足 2 位前面补 0:%tH", date));
  4. System.out.println(String.format("2 位数字 12 时制的小时,不足 2 位前面补 0:%tI", date));
  5. System.out.println(String.format("2 位数字 24 时制的小时,前面不补 0:%tk", date));
  6. System.out.println(String.format("2 位数字 12 时制的小时,前面不补 0:%tl", date));
  7. System.out.println(String.format("2 位数字的分钟,不足 2 位前面补 0:%tM", date));
  8. System.out.println(String.format("2 位数字的秒,不足 2 位前面补 0:%tS", date));
  9. System.out.println(String.format("3 位数字的毫秒,不足 3 位前面补 0:%tL", date));
  10. System.out.println(String.format("9 位数字的毫秒,不足 9 位前面补 0:%tN", date));
  11. System.out.println(String.format(Locale.US, "小写字母的上午或下午标记(英):%tp", date));
  12. System.out.println(String.format("小写字母的上午或下午标记(中):%tp", date));
  13. System.out.println(String.format("相对于 GMT 的 RFC822 时区的偏移量:%tz", date));
  14. System.out.println(String.format("时区缩写字符串:%tZ", date));
  15. System.out.println(String.format("1970-1-1 00:00:00 到现在所经过的秒数:%ts", date));
  16. System.out.println(String.format("1970-1-1 00:00:00 到现在所经过的毫秒数:%tQ", date));
  17. }

运行程序,输出:

  1. 2 位数字 24 时制的小时,不足 2 位前面补 023
  2. 2 位数字 12 时制的小时,不足 2 位前面补 011
  3. 2 位数字 24 时制的小时,前面不补 023
  4. 2 位数字 12 时制的小时,前面不补 011
  5. 2 位数字的分钟,不足 2 位前面补 031
  6. 2 位数字的秒,不足 2 位前面补 030
  7. 3 位数字的毫秒,不足 3 位前面补 0421
  8. 9 位数字的毫秒,不足 9 位前面补 0421000000
  9. 小写字母的上午或下午标记(英):pm
  10. 小写字母的上午或下午标记(中):pm
  11. 相对于 GMT RFC822 时区的偏移量:+0800
  12. 时区缩写字符串:CST
  13. 1970-1-1 00:00:00 到现在所经过的秒数:1621956690
  14. 1970-1-1 00:00:00 到现在所经过的毫秒数:1621956690421

日期格式化转换符

下表是用于格式化日期的转换字符:

转换符 说明 示例
‘b’ or ‘h’ 月份简称。 中:十月
英:Oct
‘B’ 月份全称。 中:十月
英:October
‘a’ 星期的简称。 中:星期六
英:Sat
‘A’ 星期的全称。 中:星期六
英:Saturday
‘C’ 年的前两位数字,不足两位前面补 0。 20
‘y’ 年的后两位数字,不足两位前面补 0。 21
‘Y’ 4 位数字的年份,不足 4 位前面补 0。 2021
‘j’ 一年中的天数,即年的第几天。 300
‘m’ 两位数字的月份,不足两位前面补 0。 01
‘d’ 月份的日,两位数字,不足两位前面补 0。 05
‘e’ 月份的日,前面不补 0。 5

结合例子来理解上面的语法。

  1. public static void main(String[] args) {
  2. Date date = new Date();
  3. System.out.println(String.format(Locale.US, "月份简称,英文:%tb", date));
  4. System.out.println(String.format(Locale.US, "月份简称,英文:%th", date));
  5. System.out.println(String.format(Locale.SIMPLIFIED_CHINESE, "月份简称,中文:%tb", date));
  6. System.out.println(String.format(Locale.SIMPLIFIED_CHINESE, "月份简称,中文:%th", date));
  7. System.out.println(String.format(Locale.US, "月份全称,英文:%tB", date));
  8. System.out.println(String.format(Locale.SIMPLIFIED_CHINESE, "月份全称,中文:%tB", date));
  9. System.out.println(String.format(Locale.US, "星期的简称,英文:%ta", date));
  10. System.out.println(String.format(Locale.SIMPLIFIED_CHINESE, "星期的简称,英文:%ta", date));
  11. System.out.println(String.format(Locale.US, "星期的全称,英文:%tA", date));
  12. System.out.println(String.format(Locale.SIMPLIFIED_CHINESE, "星期的全称,英文:%tA", date));
  13. System.out.println(String.format("年的前两位数字,不足两位前面补 0:%tC", date));
  14. System.out.println(String.format("年的后两位数字,不足两位前面补 0:%ty", date));
  15. System.out.println(String.format("4 位数字的年份,不足 4 位前面补 0:%tY", date));
  16. System.out.println(String.format("一年中的天数,即年的第几天:%tj", date));
  17. System.out.println(String.format("两位数字的月份,不足两位前面补 0:%tm", date));
  18. System.out.println(String.format("月份的日,两位数字,不足两位前面补 0:%td", date));
  19. System.out.println(String.format("月份的日,前面不补 0:%te", date));
  20. }

运行程序,输出:

  1. 月份简称,英文:May
  2. 月份简称,英文:May
  3. 月份简称,中文:五月
  4. 月份简称,中文:五月
  5. 月份全称,英文:May
  6. 月份全称,中文:五月
  7. 星期的简称,英文:Wed
  8. 星期的简称,英文:星期三
  9. 星期的全称,英文:Wednesday
  10. 星期的全称,英文:星期三
  11. 年的前两位数字,不足两位前面补 020
  12. 年的后两位数字,不足两位前面补 021
  13. 4 位数字的年份,不足 4 位前面补 02021
  14. 一年中的天数,即年的第几天:146
  15. 两位数字的月份,不足两位前面补 005
  16. 月份的日,两位数字,不足两位前面补 026
  17. 月份的日,前面不补 026

日期时间格式化转换符

下表是用于格式化日期时间的转换字符:

转换符 说明 示例
‘c’ 包括全部日期和时间信息 星期三 五月 26 14:48:23 CST 2021
‘F’ “年-月-日” 格式 2021-05-26
‘D’ “月/日/年” 格式 05/26/2021
‘r’ “HH:MM:SS PM” 格式,12 时制 02:48:23 下午
‘T’ “HH:MM:SS” 格式,24 时制 14:48:23
‘R’ “HH:MM” 格式,24 时制 14:48

结合例子来理解上面的语法。

  1. public static void main(String[] args) throws ParseException {
  2. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  3. Date date = sdf.parse("2021-05-26 14:48:23");
  4. System.out.println(String.format("包括全部日期和时间信息:%tc", date));
  5. System.out.println(String.format("\"年-月-日\" 格式:%tF", date));
  6. System.out.println(String.format("\"月/日/年\" 格式:%tD", date));
  7. System.out.println(String.format("\"HH:MM:SS PM\" 格式,12 时制:%tr", date));
  8. System.out.println(String.format("\"HH:MM:SS\" 格式,24 时制:%tT", date));
  9. System.out.println(String.format("\"HH:MM\" 格式,24 时制:%tR", date));
  10. }

运行程序,输出:

  1. 包括全部日期和时间信息:Wed May 26 14:48:23 CST 2021
  2. "年-月-日" 格式:2021-05-26
  3. "月/日/年" 格式:05/26/21
  4. "HH:MM:SS PM" 格式,12 时制:02:48:23 PM
  5. "HH:MM:SS" 格式,24 时制:14:48:23
  6. "HH:MM" 格式,24 时制:14:48

总结

对于字符串格式化的使用,做如下总结:

  1. String 类提供了两个格式化重载方法(format);
  2. System.out.printf(String format, Object ... args) 方法也支持与字符串格式化一样的语法;
  3. 字符串格式化的语法比较复杂,一般我们也只会用到 conversion 选项,所以把它记住用好就可以了,特殊的一些使用场景,参考上文中的语法规则实现;
  4. 一旦我们在格式化字符串中使用了 argument_index,则所有占位符都要加上;
  5. 参数 width 不能设置为 0,否则可能会报错;
  6. 不能对整型类型参数设置 precision(精度),否则会抛出异常;
  7. 部分简单日期时间类型的格式化可以直接使用 String.format() 提供的方式,不用再自己手动格式化了。

作者:殷建卫 链接:https://www.yuque.com/yinjianwei/vyrvkf/np0z31 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。