浅谈PrintStream和PrintWriter的区别和联系

网友投稿 287 2022-12-12


浅谈PrintStream和PrintWriter的区别和联系

前言

这两者在往文件中写入字符串时,最终都需要通过字符集的映射关系得到对应字节。

但这二者在通过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.aMjHShAZxYflushBuffer();

charOut.flushBuffer();

if (autoFlush && (s.indexOf('\n') >= 0))

out.flush();

}

}

...

}

也就是说,这句new PrintStream( new BufferedOutputStream( new FileOutputStream("BasicFileOutput.out")));实际上会产生五个流对象(装饰器模式会套五层,禁止套娃!)。看write函数,每次写入字符串时,竟然是先通过装饰器最外层的BufferedWriter进行的写入,然后再执行BufferedWriter、OutputStreamWriter的flushBuffer函数把字符转换为字节(像挤牙膏一样,只不过这是从外往里挤),并且将字节弄到this对象里面。

PrintStream的三种类型(File、OutputStream、String)的构造器都可以带字符集。

我们随便看一个带字符集的构造器,发现字符集被设置在this的外面一层流上,所以通过OutputStreamWriter向this传递字节数组时,这个字节数组就已经经过了特定字符集charset的encode了:

private PrintStream(boolean autoFlush, OutputStream out, Charset charset) {

super(out);

this.autoFlush = autoFlush;

this.charOut = new OutputStreamWriter(this, charset);

this.textOut = new BufferedWriter(charOut);

}

PrintWriter相比PrintStream多了一种类型的构造器(File、OutputStream、String、Writer),那就是它还可以接受一个Writer。

二者的方法分析

对比二者的api文档,你会发现前面的方法签名,这二者都是一模一样,除了后面的write方法们,所以我们只看不同的部分。相同的部分占大部分,同学们可以自行查看。

PrintStream的write方法们

从方法的描述也可以看出,这些write方法都是针对byte或者byte[ ]的。

发现有一个write方法是从FilterOutputStream继承而来,因为这个重载版本的write方法PrintStream没有去重写父类方法(下面这两个就是重写了FilterOutputStream了的)。

FilterOutputStream的write(byte[] b)版本如下:

PrintWriter的write方法们

从方法的描述也可以看出,这些write方法都是针对char或者string的。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:java实现时钟效果
下一篇:使用IDEA将Java/Kotliin工程导出Jar包的正确姿势
相关文章

 发表评论

暂时没有评论,来抢沙发吧~