说在前面:本文大部分内容和代码来自《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 散列码(十六进制) % 字符“%”