String 的 format() 方法用来将指定对象格式化为指定格式的字符串,该方法定义如下:
static String format(Locale l, String format, Object... args)
使用指定的区域设置(locale)、格式字符串(format)和参数(args)返回格式化的字符串。args 表示格式字符串(format参数)中格式说明符引用的参数。如果比格式说明符更多的参数,额外的参数将被忽略。注意,参数的数量是可变的,可能为零。
static String format(String format, Object... args)
使用指定的格式字符串和参数返回格式化的字符串。注意,默认总是使用 Locale.getDefault() 方法返回的区域设置。
格式化一个浮点数,要求浮点数保留两位小数,如下:
import java.util.Locale; public class Demo { public static void main(String[] args) { // 保留两位小数 String str = String.format("%.2f", 128.5674); System.out.println("str=" + str); str = String.format(Locale.getDefault(), "%.2f", 128.5674); System.out.println("str=" + str); } }
运行结果:
str=128.57 str=128.57
从输出可知,format() 对小数点位数截取时采用了四舍五入。
我们可以通过 IDEA 查看一下 format() 的源码,看看是怎样实现的,对我们理解更有帮助。源码如下:
public static String format(String format, Object... args) { return new Formatter().format(format, args).toString(); } public static String format(Locale l, String format, Object... args) { return new Formatter(l).format(format, args).toString(); }
从源码可知,format() 内部均是通过创建 Formatter 的实例,调用它的 format() 方法来实现,接下来我们将仔细看看 Formatter 类的用法。
Formatter 类是 printf 风格格式字符串的解释器。此类提供了对布局对齐和排列的支持,以及对数值、字符串和日期/时间数据的常规格式和特定于语言环境的输出的支持。支持诸如 byte、BigDecimal 和 Calendar 等常见 Java 类型。任意用户类型的受限格式化定制都是通过 Formattable 接口提供的。
Java 语言的格式化输出在很大程度上受到 C 语言 printf 的启发。虽然一些格式字符串与 C 类似,但已进行了某些定制,以适应 Java 语言,并且利用了其中一些特性。此外,Java 的格式比 C 的格式更严格;例如,如果转换与标志不兼容,则会抛出异常。在 C 中,不适用的标志会被忽略。
注意:对于多线程访问,Formatter 不是线程安全的。
用法示例:
StringBuilder sb = new StringBuilder(); // 将所有的输出发送到 Appendable 对象 sb 中 Formatter formatter = new Formatter(sb, Locale.US); // 明确的参数索引可用于重新排列输出顺序 formatter.format("%4$2s %3$2s %2$2s %1$2s", "a", "b", "c", "d") // -> " d c b a" // 可选的 locale 作为第一个参数,可用于获得针对本地化的数字格式。 // 可以给出精度和宽度,以便对数值进行舍入和对齐。 formatter.format(Locale.FRANCE, "e = %+10.4f", Math.E); // -> "e = +2,7183" // 其中,10 表示宽度,4 表示精度 // '(' 数字标志可用于用括号而不是减号格式化负数。会自动插入组分隔符。 formatter.format("Amount gained or lost since last statement: $ %(,.2f", balanceDelta); // -> "Amount gained or lost since last statement: $ (6,217.58)"
便捷用法示例:
// 写一个格式化的字符串到 System.out System.out.format("Local time: %tT", Calendar.getInstance()); // -> "Local time: 13:34:18" // 将格式化的输出写入 System.err System.err.printf("Unable to open file '%1$s': %2$s", fileName, exception.getMessage()); // -> "Unable to open file 'food': No such file or directory"
与 C 语言的 sprintf() 类似,可以使用静态方法 String.format() 来格式化字符串:
// 格式化一个包含日期的字符串 import java.util.Calendar; import java.util.GregorianCalendar; import static java.util.Calendar.*; Calendar c = new GregorianCalendar(1995, MAY, 23); String s = String.format("Duke's Birthday: %1$tm %1$te,%1$tY", c); // -> s == "Duke's Birthday: May 23, 1995"
产生格式化输出的每个方法都需要格式字符串和参数列表。格式字符串是一个 String,它可以包含固定文本以及一个或多个嵌入的格式说明符。如下:
Calendar c = ...; String s = String.format("Duke's Birthday: %1$tm %1$te,%1$tY", c);
此格式字符串是 format 方法的第一个参数。它包含三个格式说明符 "%1$tm"、"%1$te" 和 "%1$tY",它们指出应该如何处理参数以及在文本的什么地方插入它们。格式字符串的其余部分是包括 "Dukes Birthday: " 和其他任何空格或标点符号的固定文本。参数列表由传递给位于格式字符串之后的方法的所有参数组成。在上述示例中,参数列表的大小为 1,由对象 Calendar c 组成。
语法如下:
%[argument_index$][flags][width][.precision]conversion
其中:
可选的 argument_index 是一个十进制整数,用于表明参数在参数列表中的位置。第一个参数由 "1$" 引用,第二个参数由 "2$" 引用,依此类推。
可选的 flags 是修改输出格式的字符集,有效标志集取决于转换类型。
可选的 width 是一个非负十进制整数,表明要向输出中写入的最少字符数。
可选的 precision 是一个非负十进制整数,通常用来限制字符数。特定行为取决于转换类型。
conversion 是一个表明应该如何格式化参数的字符,给定参数的有效转换集取决于参数的数据类型。
语法如下:
%[argument_index$][flags][width]conversion
其中:
可选的 argument_index、flags 和 width 的定义同上。
conversion 是一个由两字符组成的序列。第一个字符是 't' 或 'T'。第二个字符表明所使用的格式。这些字符类似于但不完全等同于那些由 GNU date 和 POSIX strftime(3c) 定义的字符。
语法如下:
%[flags][width]conversion
其中:
可选 flags 和 width 的定义同上。
conversion 是一个表明要在输出中所插内容的字符。
下面列举了可用于 conversion 参数的转换值,转换可分为以下几类:
可应用于任何参数类型,取值如下:
'b', 'B':如果参数 arg 为 null,则结果为 "false"。如果 arg 是一个 boolean 值或 Boolean,则结果为 String.valueOf() 返回的字符串。否则结果为 "true"。
'h', 'H':如果参数 arg 为 null,则结果为 "null"。否则,结果为调用 Integer.toHexString(arg.hashCode()) 得到的结果。
's', 'S':如果参数 arg 为 null,则结果为 "null"。如果 arg 实现 Formattable,则调用 arg.formatTo()。否则,结果为调用 arg.toString() 得到的结果。
可应用于表示 Unicode 字符的基本类型:char、Character、byte、Byte、short 和 Short。当 Character.isValidCodePoint(int) 返回 true 时,可将此转换应用于 int 和 Integer 类型。取值如下:
'c', 'C':结果是一个 Unicode 字符
可应用于 Java 的整数类型 byte、Byte、short、Short、int、Integer、long、Long 和 BigInteger。取值如下:
'd':结果被格式化为十进制整数
'o':结果被格式化为八进制整数
'x', 'X':结果被格式化为十六进制整数
可用于 Java 的浮点类型 float、Float、double、Double 和 BigDecimal。取值如下:
'e', 'E':结果被格式化为用计算机科学记数法表示的十进制数
'f':结果被格式化为十进制数
'g', 'G':根据精度和舍入运算后的值,使用计算机科学记数形式或十进制格式对结果进行格式化。
'a', 'A':结果被格式化为带有效位数和指数的十六进制浮点数
可应用于 Java 能够对日期或时间进行编码的类型 long、Long、Calendar 和 Date。取值如下:
't', 'T':日期和时间转换字符的前缀
以下转换字符用来格式化时间:
'H' 24 小时制的小时,被格式化为必要时带前导零的两位数,即 00 - 23。
'I' 12 小时制的小时,被格式化为必要时带前导零的两位数,即 01 - 12。
'k' 24 小时制的小时,即 0 - 23。
'l' 12 小时制的小时,即 1 - 12。
'M' 小时中的分钟,被格式化为必要时带前导零的两位数,即 00 - 59。
'S' 分钟中的秒,被格式化为必要时带前导零的两位数,即 00 - 60 ("60" 是支持闰秒所需的一个特殊值)。
'L' 秒中的毫秒,被格式化为必要时带前导零的三位数,即 000 - 999。
'N' 秒中的毫微秒,被格式化为必要时带前导零的九位数,即 000000000 - 999999999。
'p' 特定于语言环境的 上午或下午 标记以小写形式表示,例如 "am" 或 "pm"。使用转换前缀 'T' 可以强行将此输出转换为大写形式。
'z' 相对于 GMT 的 RFC 822 格式的数字时区偏移量,例如 -0800。
'Z' 表示时区缩写形式的字符串。Formatter 的语言环境将取代参数的语言环境(如果有)。
's' 自协调世界时 (UTC) 1970 年 1 月 1 日 00:00:00 至现在所经过的秒数,即 Long.MIN_VALUE/1000 与 Long.MAX_VALUE/1000 之间的差值。
'Q' 自协调世界时 (UTC) 1970 年 1 月 1 日 00:00:00 至现在所经过的毫秒数,即 Long.MIN_VALUE 与 Long.MAX_VALUE 之间的差值。
以下转换字符用来格式化日期:
'B' 特定于语言环境的月份全称,例如 "January" 和 "February"。
'b' 特定于语言环境的月份简称,例如 "Jan" 和 "Feb"。
'h' 与 'b' 相同。
'A' 特定于语言环境的星期几全称,例如 "Sunday" 和 "Monday"
'a' 特定于语言环境的星期几简称,例如 "Sun" 和 "Mon"
'C' 除以 100 的四位数表示的年份,被格式化为必要时带前导零的两位数,即 00 - 99
'Y' 年份,被格式化为必要时带前导零的四位数(至少),例如,0092 等于格里高利历的 92 CE。
'y' 年份的最后两位数,被格式化为必要时带前导零的两位数,即 00 - 99。
'j' 一年中的天数,被格式化为必要时带前导零的三位数,例如,对于格里高利历是 001 - 366。
'm' 月份,被格式化为必要时带前导零的两位数,即 01 - 13。
'd' 一个月中的天数,被格式化为必要时带前导零两位数,即 01 - 31
'e' 一个月中的天数,被格式化为两位数,即 1 - 31。
以下转换字符用于格式化常见的日期/时间组合:
'R' 24 小时制的时间,被格式化为 "%tH:%tM"
'T' 24 小时制的时间,被格式化为 "%tH:%tM:%tS"。
'r' 12 小时制的时间,被格式化为 "%tI:%tM:%tS %Tp"。上午或下午标记 ('%Tp') 的位置可能与语言环境有关。
'D' 日期,被格式化为 "%tm/%td/%ty"。
'F' ISO 8601 格式的完整日期,被格式化为 "%tY-%tm-%td"。
'c' 日期和时间,被格式化为 "%ta %tb %td %tT %tZ %tY",例如 "Sun Jul 20 16:17:00 EDT 1969"。
产生字面值 '%' ('u0025'),取值如下:
'%':结果为字面值 '%' ('u0025')
产生特定于平台的行分隔符,取值如下:
'n':结果为特定于平台的行分隔符
下表总结了受支持的标志(flags)。如下:
'-':结果将是左对齐的
'#' :结果应该使用依赖于转换类型的替换形式,取决于 Formattable 的定义
'-':结果将是左对齐的
'-':结果将是左对齐的
'#' :结果应该使用依赖于转换类型的替换形式,只适用于 'o'、'x' 和 'X' 转换。
'+':结果总是包括一个符号,对 BigInteger 应用 'd'、'o'、'x' 和 'X' 转换时,或者对 byte 及 Byte、short 及 Short、int 及 Integer、long 及 Long 分别应用 'd' 转换时适用。
' ':对于正值,结果中将包括一个前导空格。对 BigInteger 应用 'd'、'o'、'x' 和 'X' 转换时,或者对 byte 及 Byte、short 及 Short、int 及 Integer、long 及 Long 分别应用 'd' 转换时适用。
'0':结果将用零来填充
',':结果将包括特定于语言环境的组分隔符。只适用于 'd' 转换。
'(':结果将是用圆括号括起来的负数。对 BigInteger 应用 'd'、'o'、'x' 和 'X' 转换时,或者对 byte 及 Byte、short 及 Short、int 及 Integer、long 及 Long 分别应用 'd' 转换时适用。
'-':结果将是左对齐的
'#' :结果应该使用依赖于转换类型的替换形式
' ':对于正值,结果中将包括一个前导空格
'0':结果将用零来填充
',':结果将包括特定于语言环境的组分隔符。只适用于 'e'、'E'、'f'、'g' 和 'G' 转换。
'(':结果将是用圆括号括起来的负数。只适用于 'e'、'E'、'f'、'g' 和 'G' 转换。
'-':结果将是左对齐的
宽度是将向输出中写入的最少字符数。对于行分隔符转换,不适用宽度,如果提供宽度,则会抛出异常。
对于常规参数类型,精度是将向输出中写入的最多字符数。
对于浮点转换 'e'、'E' 和 'f',精度是小数点分隔符后的位数。如果转换是 'g' 或 'G',那么精度是舍入计算后所得数值的所有位数。如果转换是 'a' 或 'A',则不必指定精度。
对于字符、整数和日期/时间参数类型转换,以及百分比和行分隔符转换,精度是不适用的;如果提供精度,则会抛出异常。
参数索引是一个十进制整数,用于表明参数在参数列表中的位置。第一个参数由 "1$" 引用,第二个参数由 "2$" 引用,依此类推。
根据位置引用参数的另一种方法是使用 '<' ('u003c') 标志,这将会重用以前格式说明符的参数。例如:以下两条语句产生的字符相同:
Calendar c = ...; String s1 = String.format("Duke's Birthday: %1$tm %1$te,%1$tY", c); String s2 = String.format("Duke's Birthday: %1$tm %<te,%<tY", c);
// 常规类型 System.out.println(String.format("%b", true)); // true System.out.println(String.format("%h", "1024")); // 1700a1 散列码,即 hashcode System.out.println(String.format("%s", "hello")); // hello // 字符类型 System.out.println(String.format("%c", 65)); // A System.out.println(String.format("%s", "hello world")); // hello world System.out.println(String.format("%2$s", "hello", "world")); // world // 数字类型 System.out.println(String.format("%10.4f", Math.PI)); // 3.1416 System.out.println(String.format("%-10.4f", Math.PI) + "|"); // 3.1416 | System.out.println(String.format("%#x", 1024)); // 0x400 十六进制 System.out.println(String.format("%#o", 1024)); // 02000 八进制 System.out.println(String.format("%e", Float.MAX_VALUE)); // 3.402823e+38 System.out.println(String.format("%g", Math.PI)); // 3.14159 System.out.println(String.format("%a", Math.PI)); // 0x1.921fb54442d18p1 System.out.println(String.format("%(d", -1024)); // (1024) 负数表示 System.out.println(String.format("%(d", 1024)); // 1024 System.out.println(String.format("%010d", 1024)); // 0000001024 使用0填充 System.out.println(String.format("% 10d", 1024)); // 1024 使用空格填充 System.out.println(String.format("%+d %d %+d %d", 10, 10, -10, -10)); // +10 10 -10 -10 // 日期/时间类型 System.out.println(String.format("%tF", new Date())); // 2023-03-29 System.out.println(String.format("%tT", new Date())); // 12:47:22 System.out.println(String.format("%1$tY-%1$tm-%1$td", new Date())); // 2023-03-29 System.out.println(String.format("%1$tH:%1$tM:%1$tS", new Date())); // 12:47:22 System.out.println(String.format("%1$tH:%<tM:%<tS", new Date())); // 12:47:22
%s 字符串类型 %c 字符类型
%d 十进制整数 %x 十六进制整数
%o 八进制整数 %b 布尔值类型
%f 浮点数 %a 十六进制浮点数
%g 通用浮点数 %e 指数形式
%h 散列码 %% 百分号
%n 换行