说在前面:本文大部分内容和代码来自《Thinking in Java》。这本书真的是一本好书,强烈推荐。
在Java SE5(很久很久以前的版本)引入了format方法,这个方法跟C的printf方法很像。首先用个简单例子说明下如何使用format方法。
public static void main(String[] args) {
int x = 5;
double y = 5.129867;
//旧的方式
System.out.println("Row 1: [" + x + " " + y + "]");
//新的方式
System.out.format("Row 1: [%d %f]\n", x, y);
//或
System.out.printf("Row 1: [%d %f]\n", x, y);
}
输出结果
Row 1: [5 5.129867]
Row 1: [5 5.129867]
Row 1: [5 5.129867]
使用很简单,一个简单的格式化字符串,加上一串参数即可。format和printf是等价的。我们可以看下printf方法的实现。
public PrintStream printf(String format, Object ... args) {
return format(format, args);
}
再来看下 format方法的实现
private Formatter formatter;
……
public PrintStream format(String format, Object ... args) {
try {
synchronized (this) {
ensureOpen();
if ((formatter == null)
|| (formatter.locale() != Locale.getDefault()))
formatter = new Formatter((Appendable) this);
formatter.format(Locale.getDefault(), format, args);
}
} catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
} catch (IOException x) {
trouble = true;
}
return this;
}
可以看到,format是通过Formatter类来实现内容格式化的。
在Java中,所有新的格式化功能都由java.util.Formatter类处理,可以将Formatter看作一个翻译器,它将你的格式化字符串与数据翻译成需要的结果。当你创建一个Formatter 对象的时候,需要向其构造器传递一些信息,告诉它最终的结果将向哪里输出。
再看一个例子,这次我们不直接使用 System.out.format,而是使用 java.util.Formatter来实现格式化输出内容的需求:
public class Turtle {
private String name;
private Formatter f;
public Turtle(String name, Formatter f){
this.name = name;
this.f = f;
}
public void move(int x, int y){
f.format("%s The Turtle is at (%d, %d)\n", name, x, y);
}
public static void main(String[] args) {
Turtle tommy = new Turtle("Tommy", new Formatter(System.out));
tommy.move(0, 0);
tommy.move(3, 2);
tommy.move(7, 2);
}
}
输出结果:
Tommy The Turtle is at (0, 0)
Tommy The Turtle is at (3, 2)
Tommy The Turtle is at (7, 2)
Formatter的构造器可以接受多种输出目的地,比如上例中的PrintStream,或者OutputStream和File等。有兴趣的可以查看Formatter的构造器。
看到这里你可能会想:在上面的例子中,只是简单的输出字符串句子,以前常用的字符串拼接也可以做到,而且并没有多麻烦呀?
那我们现在来看下Formatter的更强大的地方:控制空格与对齐。
解释再多,不如来一个实例,我们看例子:
public class Receipt {
private double total = 0;
private Formatter f = new Formatter(System.out);
public void printTitle(){
f.format("%-15s %5s %10s\n", "Item", "Qty", "Price");
f.format("%-15s %5s %10s\n", "----", "---", "-----");
}
public void print(String name, int qty, double price){
f.format("%-15.15s %5d %10.2f\n", name, qty, price);
total = total + price;
}
public void printTotal(){
f.format("%-15s %5s %10.2f\n", "Tax", "", total*0.06);
f.format("%-15s %5s %10s\n", "", "", "-----");
f.format("%-15s %5s %10.2f\n", "Total", "", total*1.06);
}
public static void main(String[] args) {
Receipt receipt = new Receipt();
receipt.printTitle();
receipt.print("Jack's Magic Beans", 4, 4.25);
receipt.print("Princess Peas", 3, 5.1);
receipt.print("Three Bears Proridge", 1, 14.29);
receipt.printTotal();
}
}
输出结果:
Item Qty Price
---- --- -----
Jack's Magic Be 4 4.25
Princess Peas 3 5.10
Three Bears Pro 1 14.29
Tax 1.42
-----
Total 25.06
是不是很整齐?通过简洁的语法,Formatter提供了对空格与对齐的强大控制能力。
看完例子,我们来解释下上面的格式化字符串“%-15s %5s %10.2f\n”是啥意思。以下是其抽象的语法:
%[argument_index$][flags][width][.precision]conversion
最常见的就是通过width来控制一个域的大小。比如“%-15s”中的15。默认情况下,数据是右对齐,不过可以通过使用“-”来改变对齐方向。precision则有点特别,首先该值前面必须带小数点。比如“%10.2f”;其次应用于不同类型的数据转换时,precision的意义也不同。应用于String时,它表示打印String时输出字符的最大数量。而在将precison应用于浮点数时,则表示小数部分要显示出来的位数(默认是6位小数),如果小数位数过多则舍入,太少则在尾部补零。应用于整数,触发异常,因为整数没有小数部分。
最后补充下常用的类型转换字符:
类型转换字符表
--------------------
d 整数型(十进制)
c Unicode字符
b Boolean值
s String
f 浮点数(十进制)
e 浮点数(科学计数)
x 整数(十六进制)
h 散列码(十六进制)
% 字符“%”