Java 零基础教程

Java printf 格式化输出

格式化输出是 Java 编程中的一项关键技能,它允许你以清晰、易读且专业的方式展示数据。printf 方法(以及相关的格式化功能)让你能够精确控制数字、日期、字符串和其他数据类型的显示方式。

本章将深入探讨 printf 的细节,为你提供为各种应用程序创建精美输出的工具。我们将基于上一章关于控制台打印的知识,并为后续章节中将数据写入文件做好准备。

1. 理解 printf

printf 方法继承自 C 语言,为 Java 提供了一种强大的格式化输出方式。它允许你在字符串中嵌入格式说明符 (Format Specifiers),这些说明符随后会被相应的参数替换。

通用语法如下:

System.out.printf(格式字符串, 参数1, 参数2, ...);
  • 格式字符串 (formatString):包含字面文本和格式说明符的字符串。
  • 参数1, 参数2, ...:将被格式化并插入到格式字符串中的参数。参数的数量和类型必须与格式说明符相匹配。

如果你熟悉 println 方法,你会发现 printf 提供了更多的控制权。你不再是简单地将值拼接成字符串,而是精确指定输出的样式。

2. 格式说明符 (Format Specifiers)

格式说明符以百分号 (%) 开始,以指示参数数据类型的转换字符结束。以下是一些常用的转换字符:

转换字符数据类型描述
%dint, byte, short, long将整数格式化为十进制数字。
%ffloat, double将浮点数格式化为十进制数字。
%sString格式化字符串。
%cchar格式化字符。
%bboolean将布尔值格式化为 "true" 或 "false"。
%n(无)插入特定于平台的换行符。为了跨平台兼容性,通常建议使用 %n 而非 \n
%%(无)输出一个字面意义上的百分号 (%)。

示例:

int quantity = 10;
double price = 2.50;
String item = "苹果";
System.out.printf("我们有 %d 个 %s,每个售价为 $%.2f。%n", quantity, item, price);
// 输出: 我们有 10 个 苹果,每个售价为 $2.50。

在这个例子中:

  • %dquantity 的值 (10) 替换。
  • %sitem 的值 ("苹果") 替换。
  • %.2fprice 的值 (2.50) 替换,并格式化为两位小数。
  • %n 插入一个换行符。

3. 标志、宽度与精度

格式说明符可以包含标志、宽度和精度修饰符,以进一步自定义输出。格式说明符的完整语法为:

%[标志][宽度][.精度]转换字符

3.1 标志 (Flags)

用于修改输出格式。常用标志包括:

  • - (左对齐):在指定的宽度内左对齐输出。
  • + (包含符号):始终为数值包含正负号(+-)。
  • 0 (零填充):用前导零填充输出以达到指定宽度。
  • , (分组分隔符):为数字使用特定于语言环境的分组分隔符(例如:美国用逗号,欧洲用点)。
  • 空格:在没有 + 标志的正数前添加一个空格。

3.2 宽度 (Width)

指定输出的最小字符数。如果值的长度小于宽度,则会用空格(或在使用 0 标志时用零)填充。

3.3 精度 (Precision)

指定浮点数小数点后的位数,或字符串输出的最大字符数。

代码示例:

int number = 123;
double value = 1234.5678;
String text = "Hello";

System.out.printf("宽度为 6 的数字: %6d%n", number);       // 输出: "   123"
System.out.printf("零填充数字: %06d%n", number);          // 输出: "000123"
System.out.printf("精度为 2 的数值: %.2f%n", value);      // 输出: "1234.57" (注意四舍五入)
System.out.printf("带分组分隔符的数值: %,f%n", value);   // 输出: "1,234.567800" (取决于地区)
System.out.printf("左对齐文本: %-10s!%n", text);         // 输出: "Hello     !"

4. 日期与时间格式化

printf 还支持使用特殊的转换字符格式化日期和时间值。这些说明符以 %t 开头。

转换字符描述示例 (基于当前日期)
%tD格式化为 MM/DD/YY03/15/26
%tF格式化为 YYYY-MM-DD2026-03-15
%tT格式化为 HH:MM:SS (24小时制)14:30:45
%tr格式化为带 AM/PM 的时间02:30:45 PM
%tA星期几的全称星期五 (取决于语言环境)
%tB月份的全称三月
%tY四位年份2026

示例:

import java.util.Date;

public class DateFormatExample {
    public static void main(String[] args) {
        Date today = new Date();
        // 使用 < 标志引用前一个参数
        System.out.printf("今天是: %tA, %tB %td, %tY%n", today, today, today, today);
        // 输出: 今天是: 星期五, 三月 15, 2026
        
        System.out.printf("当前时间: %tr%n", today);
    }
}

技巧: 如果你在同一个格式字符串中多次使用同一个日期对象,可以使用 1$ 这种形式或 < 标志来避免重复编写参数。
System.out.printf("%1$tA, %1$tB %1$td, %1$tY%n", today); 这里的 1$ 表示引用第一个参数。

5. 参数索引 (Argument Index)

你可以通过参数索引明确指定参数的使用顺序。参数索引是一个整数后跟一个 $ 符号。例如,%2$d 指向第二个参数,%3$s 指向第三个参数。

String firstName = "张";
String lastName = "三";
// 输出: 三, 张
System.out.printf("%2$s, %1$s%n", firstName, lastName);

6. 实际应用案例

6.1 案例 1:格式化表格输出

展示带有价格和数量的产品列表。

public class ProductTable {
    public static void main(String[] args) {
        String[] products = {"笔记本电脑", "鼠标", "键盘", "显示器"};
        double[] prices = {1200.00, 25.50, 75.00, 300.75};
        int[] quantities = {5, 20, 10, 7};

        System.out.printf("%-12s %8s %8s%n", "产品名称", "价格", "数量");
        System.out.println("----------------------------------"); 
        for (int i = 0; i < products.length; i++) {
            System.out.printf("%-12s %8.2f %8d%n", products[i], prices[i], quantities[i]);
        }
    }
}

6.2 案例 2:带本地化设置的货币格式化

使用 java.util.Locale 根据不同地区的标准格式化金额。

import java.util.Locale;

public class CurrencyFormatting {
    public static void main(String[] args) {
        double amount = 1234.56;
        // 美国美元格式
        System.out.printf(Locale.US, "美国: $%,.2f%n", amount);
        // 德国欧元格式
        System.out.printf(Locale.GERMANY, "德国: %,.2f €%n", amount);
    }
}