PrintWriter是字符输出流一个重要的方法,其源码非常简单易懂。下面基于源码来分析PrintWriter跟之前的BufferWriter、FileWriter之间的区别。
构造函数
public PrintWriter(Writer out,
boolean autoFlush) {
super(out);
this.out = out;
this.autoFlush = autoFlush;
lineSeparator = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction("line.separator"));
}
public PrintWriter(OutputStream out, boolean autoFlush) {
this(new BufferedWriter(new OutputStreamWriter(out)), autoFlush);
// save print stream for error propagation
if (out instanceof java.io.PrintStream) {
psOut = (PrintStream) out;
}
}
可见,PrintWriter可以用来包装Writer的任意子类,当包装OutputStream时,默认包装一层BufferWriter。当然也可以将File或者File路径作为参数,实质上一样,如下所示:
public PrintWriter(String fileName) throws FileNotFoundException {
this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName))),
false);
}
/* Private constructor */
private PrintWriter(Charset charset, File file)
throws FileNotFoundException
{
this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), charset)),
false);
}
构造函数里的autoFlush参数默认是false,当设置true时开启自动刷新,当运行println()/printf()/format()方法时,会自动刷新缓冲区。
输出方法
public void write(String s, int off, int len) {
try {
synchronized (lock) {
ensureOpen();
out.write(s, off, len);
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
trouble = true;
}
}
如果包装了BufferWriter,则out.write(s, off, len)这一行直接转至BufferWriter的对应方法。其它char、char[]格式参数的重载方法大同小异。
输出格式化
PrintWriter中增添了许多数据格式化的代码,即兼容不同类型的变量,float、int等等,如下所示:
public void print(Object obj) {
write(String.valueOf(obj));
}
public void print(double d) {
write(String.valueOf(d));
}
实际上都是使用String.valueOf()将其转化成String,实现不同类型数据的兼容。除此之外,还对每种类型变量实现了println()功能,即换行,如下:
public void println() {
newLine();
}
public void println(long x) {
synchronized (lock) {
print(x);
println();
}
}
private void newLine() {
try {
synchronized (lock) {
ensureOpen();
out.write(lineSeparator);
if (autoFlush)
out.flush();
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
trouble = true;
}
}
newLine()输出换行,同时执行out.flush()刷新缓冲区。
PrintWriter还提供了printf方法,支持指定格式的输出,实则调用的是format方法,如下:
//Locale l表示语言地区
public PrintWriter printf(Locale l, String format, Object ... args) {
return format(l, format, args);
}
public PrintWriter format(Locale l, String format, Object ... args) {
try {
synchronized (lock) {
ensureOpen();
if ((formatter == null) || (formatter.locale() != l))
formatter = new Formatter(this, l);
formatter.format(l, format, args);
if (autoFlush)
out.flush();
}
} catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
} catch (IOException x) {
trouble = true;
}
return this;
}
同样,执行format的过程中也会造成缓冲区刷新(若autoFlush为true)。
注意:若PrintWriter没有包装BufferWriter 譬如直接包装FileWriter,这样缓冲区直接调用StreamEncoder中的flush刷新字节缓冲区;若用了BufferWriter,则调起BufferWriter中的flush函数,先刷新字符缓冲区,再同样刷新StreamEncoder中的字节缓冲区。
异常处理
PrintWriter不会抛出异常,也不需要在外部处理异常,观察前面的write函数可知,方法内部已经做了try catch操作,同时维护了一个trouble布尔变量,可利用它告知外部是否有异常。例如checkError()方法。同时与之相对应的,有setError()和clearError()方法。
public boolean checkError() {
if (out != null) {
flush();
}
if (out instanceof java.io.PrintWriter) {
PrintWriter pw = (PrintWriter) out;
return pw.checkError();
} else if (psOut != null) {
return psOut.checkError();
}
return trouble;
}
protected void setError() {
trouble = true;
}
protected void clearError() {
trouble = false;
}
append与write区别
PrintWriter中有两个append方法,如下:
public PrintWriter append(CharSequence csq) {
if (csq == null)
write("null");
else
write(csq.toString());
return this;
}
public PrintWriter append(CharSequence csq, int start, int end) {
CharSequence cs = (csq == null ? "null" : csq);
write(cs.subSequence(start, end).toString());
return this;
}
感觉这个append没什么用,跟write的区别在于,参数可以传入null,还有参数是CharSequence类型字符序列(String,StringBuilder,StringBuffer都实现了这个接口)。
PrintWriter特点总结
- 可以兼容多种变量类型的输出,可用printf自定义输出格式,也可用println换行输出;
- 设置autoflush为true时,可在换行或者format时自动刷新缓冲区;
- 不会抛出异常。