前言
这两者在往文件中写入字符串时,最终都需要通过字符集的映射关系得到对应字节。
但这二者在通过char得到对应若干字节的时机不一样,以new PrintStream( new BufferedOutputStream( new FileOutputStream("BasicFileOutput.out")));和new PrintWriter( new BufferedWriter( new FileWriter("BasicFileOutput.out")))为例,前者在存字符串时,从PrintStream传到BufferedOutputStream时就已经是字节了;后者在存字符串时,直到FileWriter真正写入文件时,才将字符转换为字节。
如果PrintStream被设置为autoFlush,那么这些情况flush方法将会自动执行:写入字节数组、任何重载版本的println被调用、一个换行符(char)被写入、一个换行符的字节存储(n)被写入。
如果PrintWriter被设置为autoFlush,那么这些情况flush方法将会自动执行:println、printf、format方法被调用。
它们都不会抛出IO异常,因为它们在方法内部捕获住了,可以通过checkError()来判断是否发生异常。
PrintWriter会使用平台特有的换行符(比如Windows和linux),PrintStream则固定使用n。
总的来说,Reader/Writer相比InputStream/OutputStream算是一种升级,将当初设计得不好的地方进行了优化。
二者的构造器分析
这二者真的很像,你可以去看它们俩的api文档,可以发现它们的构造器和方法几乎一模一样。
你会注意到PrintWriter的构造器中,也可以指定字符集,这可能有点奇怪,因为作为用来装饰的Writer(PrintWriter)来说,它应该不需要关心字符应该怎么对应到字节上去,只需要关心字符就好了呀。
PrintWriter有两个构造器都可以指定字符集:PrintWriter(File file, String csn)和PrintWriter(String fileName, String csn),但这两个构造器最终都会调用到下面这个构造器,可以看出charset是给装饰器最里层的FileOutputStream使用的,而外层的两个Writer都不用关心。这说明Writer确实不需要关心字符集。
/* Private constructor */
private PrintWriter(Charset charset, File file)
throws FileNotFoundException
{
this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), charset)),
false);
}
其实PrintStream也很low,观察它的构造器可以发现它竟然是靠BufferedWriter来驱动的(这看起来和上面贴的PrintWriter的构造器里的逻辑一样,这里指装饰器的装饰过程):
/* Private constructors */
private PrintStream(boolean autoFlush, OutputStream out) {
super(out);
this.autoFlush = autoFlush;
this.charOut = new OutputStreamWriter(this);//往自己身上装饰一层
this.textOut = new BufferedWriter(charOut);//往自己身上再装饰一层
}
private void write(String s) {
try {
synchronized (this) {
ensureOpen();
textOut.write(s);//先调用最外层的流的write函数
textOut.flushBuffer();
charOut.flushBuffer();
if (autoFlush && (s.indexOf('n') >= 0))
out.flush();
}
}
...
}










