PrintWriter因其简单易用、灵活而强大的格式化输出能力从而在字符流输出方面得到了越来越多的使用,不过以下两点还是需要强调一下:
一、PrintWriter带缓冲吗?一般而言是否带有缓冲对IO的性能影响非常大,而关于PrintWriter是否带有缓冲这一点在JDK中并没有统一而明确的阐述,下面就从两个不同的层次讨论一下这个问题:
1)BufferedWriter缓冲:事实上PrintWriter共有八个构造器,除了最后两个以Writer作为参数的构造器外,其他六个构造器都会在内部创建一个BufferedWriter,然后再使用该BufferedWriter作为参数去调用最后一个构造器从而完成PrintWriter对象的创建,因此通过前六个构造器创建的PrintWriter本身是带有BufferedWriter缓冲的,而通过后两个构造器创建的PrintWriter对象是否也有缓冲能力则取决于你提供的Writer参数本身是否带有缓冲,类实现并不自动为其提供。
2)StreamEncoder缓冲:通过前六个构造器创建的PrintWriter对象都是基于OutputStreamWriter之上的,而OutputStreamWriter又是通过sun.nio.cs.StreamEncoder包装而成的,通过查阅文档,我们可以知道StreamEncoder中已经内置了一个8KB的ByteBuffer缓冲,输出字符经过再编码后保存在该缓冲区中,缓冲满后调用本地方法整体写入IO子系统。
3)由上可知,一个PrintWriter对象不论其是否带有BufferedWriter缓冲,在字符流和IO系统之间总是存在一个ByteBuffer缓冲,这样呢BufferedWriter缓冲对PrintWriter性能的影响就变得不那么显著了,因为它只起到了减少方法调用次数的作用(实际上是降低了CPU的开销),在成串输出字符模式下这一点体现的会更加明显,因为成串输出本身已显著较少了方法的调用次数。如下例:
import java.io.*;
public class PrintWriterTest {
public static void main(String[] args) throws Exception
{
Timer timer = new Timer();
String sText = "书山有路勤为径,蜡炬成灰泪始干。问渠那得清如许?牧童遥指杏花村。";
char[] aText = sText.toCharArray();
System.out.println("每次输出操作只输出一个字符时:");
long totalTime1 = 0;
long totalTime2 = 0;
for(int i = 0; i < 20; i++) { //测试20次,取平均值
for( int j = 0; j < 1024*1024*64; j++ ) //等待IO子系统恢复平静
timer.start();
PrintWriter pw1 = new PrintWriter("d:\\测试1.txt");
for( int j = 0; j < 1024*1024; j++ ){
for( int k = 0; k < aText.length; k++)
pw1.write(aText[k]);
}
pw1.close();
timer.stop();
totalTime1 += timer.getTime();
System.out.print("使用内置BufferedWriter缓冲的PrintWriter时耗时:" + timer.getTime());
for( int j = 0; j < 1024*1024*64; j++ ) //等待IO子系统恢复平静
timer.start();
PrintWriter pw2 = new PrintWriter(new OutputStreamWriter(new FileOutputStream("d:\\测试2.txt")));
for( int j = 0; j < 1024*1024; j++ ){
for( int k = 0; k < aText.length; k++)
pw2.write(aText[k]);
}
pw2.close();
timer.stop();
totalTime2 += timer.getTime();
System.out.println(" 使用无内置BufferedWriter缓冲的PrintWriter时耗时:" + timer.getTime());
}
System.out.print("内置BufferedWriter缓冲时的耗时平均值:" + (long)(totalTime1/20));
System.out.println(" 无内置BufferedWriter缓冲时的耗时平均值:" + (long)(totalTime2/20));
System.out.println("\n----------------------------------\n");
System.out.println("每次输出操作输出一串字符时:");
totalTime1 = 0;
totalTime2 = 0;
for(int i = 0; i < 20; i++) { //测试20次,取平均值
for( int j = 0; j < 1024*1024*64; j++ ) //等待IO子系统恢复平静
timer.start();
PrintWriter pw1 = new PrintWriter("d:\\测试1.txt");
for( int j = 0; j < 1024*1024; j++ ){
pw1.write(aText);
}
pw1.close();
timer.stop();
totalTime1 += timer.getTime();
System.out.print("使用内置BufferedWriter缓冲的PrintWriter时耗时:" + timer.getTime());
for( int j = 0; j < 1024*1024*64; j++ ) //等待IO子系统恢复平静
timer.start();
PrintWriter pw2 = new PrintWriter(new OutputStreamWriter(new FileOutputStream("d:\\测试2.txt")));
for( int j = 0; j < 1024*1024; j++ ){
pw2.write(aText);
}
pw2.close();
timer.stop();
totalTime2 += timer.getTime();
System.out.println(" 使用无内置BufferedWriter缓冲的PrintWriter时耗时:" + timer.getTime());
}
System.out.print("内置BufferedWriter缓冲时的耗时平均值:" + (long)(totalTime1/20));
System.out.println(" 无内置BufferedWriter缓冲时的耗时平均值:" + (long)(totalTime2/20));
}
}
class Timer {
private long startTime, endTime;
public void start(){
startTime = System.currentTimeMillis();
}
public void stop(){
endTime = System.currentTimeMillis();
}
public long getTime(){
return endTime - startTime;
}
}
输出结果如下(单位毫秒):
每次输出操作只输出一个字符时:
使用内置BufferedWriter缓冲的PrintWriter时耗时:6344 使用无内置BufferedWriter缓冲的PrintWriter时耗时:6360
使用内置BufferedWriter缓冲的PrintWriter时耗时:2734 使用无内置BufferedWriter缓冲的PrintWriter时耗时:6469
使用内置BufferedWriter缓冲的PrintWriter时耗时:2422 使用无内置BufferedWriter缓冲的PrintWriter时耗时:6562
使用内置BufferedWriter缓冲的PrintWriter时耗时:2391 使用无内置BufferedWriter缓冲的PrintWriter时耗时:6078
使用内置BufferedWriter缓冲的PrintWriter时耗时:3109 使用无内置BufferedWriter缓冲的PrintWriter时耗时:6172
使用内置BufferedWriter缓冲的PrintWriter时耗时:2734 使用无内置BufferedWriter缓冲的PrintWriter时耗时:6359
使用内置BufferedWriter缓冲的PrintWriter时耗时:2656 使用无内置BufferedWriter缓冲的PrintWriter时耗时:6391
使用内置BufferedWriter缓冲的PrintWriter时耗时:2672 使用无内置BufferedWriter缓冲的PrintWriter时耗时:6062
使用内置BufferedWriter缓冲的PrintWriter时耗时:2578 使用无内置BufferedWriter缓冲的PrintWriter时耗时:6500
使用内置BufferedWriter缓冲的PrintWriter时耗时:2438 使用无内置BufferedWriter缓冲的PrintWriter时耗时:6063
使用内置BufferedWriter缓冲的PrintWriter时耗时:2578 使用无内置BufferedWriter缓冲的PrintWriter时耗时:6609
使用内置BufferedWriter缓冲的PrintWriter时耗时:2640 使用无内置BufferedWriter缓冲的PrintWriter时耗时:6282
使用内置BufferedWriter缓冲的PrintWriter时耗时:2609 使用无内置BufferedWriter缓冲的PrintWriter时耗时:6578
使用内置BufferedWriter缓冲的PrintWriter时耗时:2594 使用无内置BufferedWriter缓冲的PrintWriter时耗时:6250
使用内置BufferedWriter缓冲的PrintWriter时耗时:2719 使用无内置BufferedWriter缓冲的PrintWriter时耗时:6422
使用内置BufferedWriter缓冲的PrintWriter时耗时:2562 使用无内置BufferedWriter缓冲的PrintWriter时耗时:6485
使用内置BufferedWriter缓冲的PrintWriter时耗时:2609 使用无内置BufferedWriter缓冲的PrintWriter时耗时:6407
使用内置BufferedWriter缓冲的PrintWriter时耗时:2422 使用无内置BufferedWriter缓冲的PrintWriter时耗时:6437
使用内置BufferedWriter缓冲的PrintWriter时耗时:2469 使用无内置BufferedWriter缓冲的PrintWriter时耗时:6687
使用内置BufferedWriter缓冲的PrintWriter时耗时:2438 使用无内置BufferedWriter缓冲的PrintWriter时耗时:6360
内置BufferedWriter缓冲时的耗时平均值:2785 无内置BufferedWriter缓冲时的耗时平均值:6376
----------------------------------
每次输出操作输出一串字符时:
使用内置BufferedWriter缓冲的PrintWriter时耗时:2062 使用无内置BufferedWriter缓冲的PrintWriter时耗时:2578
使用内置BufferedWriter缓冲的PrintWriter时耗时:3969 使用无内置BufferedWriter缓冲的PrintWriter时耗时:2484
使用内置BufferedWriter缓冲的PrintWriter时耗时:3032 使用无内置BufferedWriter缓冲的PrintWriter时耗时:4718
使用内置BufferedWriter缓冲的PrintWriter时耗时:3140 使用无内置BufferedWriter缓冲的PrintWriter时耗时:2844
使用内置BufferedWriter缓冲的PrintWriter时耗时:4546 使用无内置BufferedWriter缓冲的PrintWriter时耗时:2563
使用内置BufferedWriter缓冲的PrintWriter时耗时:2515 使用无内置BufferedWriter缓冲的PrintWriter时耗时:4110
使用内置BufferedWriter缓冲的PrintWriter时耗时:2547 使用无内置BufferedWriter缓冲的PrintWriter时耗时:2468
使用内置BufferedWriter缓冲的PrintWriter时耗时:3812 使用无内置BufferedWriter缓冲的PrintWriter时耗时:2657
使用内置BufferedWriter缓冲的PrintWriter时耗时:2313 使用无内置BufferedWriter缓冲的PrintWriter时耗时:4438
使用内置BufferedWriter缓冲的PrintWriter时耗时:2188 使用无内置BufferedWriter缓冲的PrintWriter时耗时:2218
使用内置BufferedWriter缓冲的PrintWriter时耗时:4000 使用无内置BufferedWriter缓冲的PrintWriter时耗时:2531
使用内置BufferedWriter缓冲的PrintWriter时耗时:2328 使用无内置BufferedWriter缓冲的PrintWriter时耗时:4375
使用内置BufferedWriter缓冲的PrintWriter时耗时:2328 使用无内置BufferedWriter缓冲的PrintWriter时耗时:2547
使用内置BufferedWriter缓冲的PrintWriter时耗时:4250 使用无内置BufferedWriter缓冲的PrintWriter时耗时:2453
使用内置BufferedWriter缓冲的PrintWriter时耗时:3578 使用无内置BufferedWriter缓冲的PrintWriter时耗时:3047
使用内置BufferedWriter缓冲的PrintWriter时耗时:2329 使用无内置BufferedWriter缓冲的PrintWriter时耗时:4188
使用内置BufferedWriter缓冲的PrintWriter时耗时:3219 使用无内置BufferedWriter缓冲的PrintWriter时耗时:2531
使用内置BufferedWriter缓冲的PrintWriter时耗时:2875 使用无内置BufferedWriter缓冲的PrintWriter时耗时:2719
使用内置BufferedWriter缓冲的PrintWriter时耗时:2453 使用无内置BufferedWriter缓冲的PrintWriter时耗时:3157
使用内置BufferedWriter缓冲的PrintWriter时耗时:2563 使用无内置BufferedWriter缓冲的PrintWriter时耗时:2563
内置BufferedWriter缓冲时的耗时平均值:3002 无内置BufferedWriter缓冲时的耗时平均值:3059
二、为了使用的简单性,除了构造器外,PrintWriter类的其他方法并不抛出I/O异常,要得知IO操作过程是否出错,可通过调用checkError()方法进行查询,注意该方法同时清洗缓冲区。也正是由于这一点,我们最好把PrintWriter看成是面向最终用户的类,不要在其上再进行什么包装,例如下面两句虽然都合法,也可顺利编译,但是很明显第二句那种用法是有缺陷的:
new PrintWriter(new BufferedWriter(new FileWriter("test.txt")));
new BufferedWriter(new PrintWriter(new FileWriter("test.txt")));