FileWriter与BufferedWriter的适用场景
IO这块,各种Writer,Reader,让人眼晕
而在网上基本找不到在什么时候用哪个类,并且网上的IO demo 很多用法都是错的
在这简单的分析一下FileWriter与BufferedWriter
一、两个类的继承关系
FileWriter | BufferedWriter |
java.lang.Object 𠃊 java.io.Writer 𠃊 java.io.OutputStreamWriter 𠃊 java.io.FileWriter |
java.lang.Object 𠃊 java.io.Writer 𠃊 java.io.BufferedWriter |
由继承关系简单的来看,FileWriter是非常有意思的,它继承了OutputStreamWriter,这意味着它的写入是由FileOutputStream实现的,
这与我在前一篇《字节流与字符流的一些个人看法》中写到的内容相符合:字节用来与文件打交道,而字符用来和人打交道。
简单的说,FileWriter它的存在价值就在于方便人们写入字符串(即人能看懂的东西),它的内部依然是由FileOutputStream实现。
而BufferedWriter直接继承于java.io.Writer,看它的构造函数与用法,明显使用了装饰设计模式,
这意味着我们要使用它,就必须给他一个Writer才可以,在该文章中我们假设给定的Writer就是FileWriter。
二、两个类的用法
FileWriter | BufferedWriter |
FileWriter fw = null; try { fw = new FileWriter("c:/123456"); fw.write("0123456789"); fw.flush(); } catch (IOException e) { e.printStackTrace(); } finally{ //关闭FileWriter try { if(null != fw) fw.close(); } catch (IOException e) { e.printStackTrace(); } }
|
FileWriter fw = null; BufferedWriter bw = null; try { fw = new FileWriter("c:/123456"); //装饰模式,bw为包装对象,fw为真实对象 bw = new BufferedWriter(fw); bw.write("0123456789"); bw.flush(); } catch (IOException e) { e.printStackTrace(); } finally{ //关闭BufferedWriter try { if(null != bw) bw.close(); } catch (IOException e) { e.printStackTrace(); } //关闭FileWriter try { if(null != fw) fw.close(); } catch (IOException e) { e.printStackTrace(); } }
|
从两个类的用法上来看,一个直接创建了FileWriter对象,写入数据
另一个,创建了FileWriter对象后,又创建了BufferedWriter对象,利用装饰模式操作BufferedWriter对象写入数据
同样的功能,两种方式都能实现,那么BufferedWriter存在的意义是什么?
三、BufferedWriter存在的意义
在网上能找到的答案基本都是: BufferedWriter输出时有缓冲区。
但是FileWriter也有啊,他们区别仅仅是FileWriter缓冲区大小为1024,而BufferedWriter默认缓冲区大小为8192,并且可以自己设置
为了测试,下面的程序分别用FileWriter、和BufferedWriter输出10M的字符串
FileWriter | BufferedWriter |
//接近10M的字符串 StringBuilder sb = new StringBuilder(); for(int i = 0; i < 10000000; i++){ sb.append("0123456789"); } FileWriter fw = null; try { fw = new FileWriter("c:/123456"); fw.write(sb.toString()); fw.flush(); } catch (IOException e) { e.printStackTrace(); } finally{ //关闭FileWriter try { if(null != fw) fw.close(); } catch (IOException e) { e.printStackTrace(); } }
|
//接近10M的字符串 StringBuilder sb = new StringBuilder(); for(int i = 0; i < 10000000; i++){ sb.append("0123456789"); } FileWriter fw = null; BufferedWriter bw = null; try { fw = new FileWriter("c:/123456"); //装饰模式,bw为包装对象,fw为真实对象 bw = new BufferedWriter(fw); bw.write(sb.toString()); bw.flush(); } catch (IOException e) { e.printStackTrace(); } finally{ //关闭BufferedWriter try { if(null != bw) bw.close(); } catch (IOException e) { e.printStackTrace(); } //关闭FileWriter try { if(null != fw) fw.close(); } catch (IOException e) { e.printStackTrace(); } }
|
714ms | 600ms |
从结果来看,他们的效率是有区别的,但区别并没到悬殊的地步
所以,这并不是他们区别的关键,在阅读两个类的源码后,我认为他们的最关键的区别在于:
FileWriter每次调用write()方法,就会调用一次OutputStreamWriter中的write()方法
而BufferedWriter只有在缓冲区满了才会调用OutputStreamWriter中的write()方法
为此我重写了FileWriter与OutputStreamWriter两个类,用来测试调用10000000次write()方法时OutputStreamWriter的write()方法执行次数
测试结果如下:
FileWriter | BufferedWriter | |
OutputStreamWrite的write()方法执行次数 | 10000000次 | 12207次 |
时间 | 1387ms | 718ms |
四、总结
BufferedWriter的执行速度确实要比FileWriter快,这方面的原因我还没找到。
但BufferedWriter在执行过程中增加了一层缓冲区,用来避免频繁执行OutputStreamWriter的write()方法
所以:
在一次性写入字符串到文件时可以直接调用FileWriter,在效率上影响不大
需多次写入字符串到文件时应调用BufferedWriter,可以避免频繁执行OutputStreamWriter的write()方法
五、题外话
网上能找到的区别大致上是
1、BufferedWriter输出时有缓冲区
2、BufferedWriter的write方法可以避免频繁的硬盘读写
这两种说法其实都是错误的
第一种:无论FileWriter还是BufferedWriter都有缓冲区,而缓冲区的大小1024已经足够
第二种:BufferedWriter的write方法无法实现避免频繁的硬盘读写,因为OutputStreamWriter的write()方法调用了StreamEncoder的write方法,而它的实现有缓冲区的存在,
所以只有在缓冲区满的情况下才会写入硬盘,因此FileWriter还是BufferedWriter的硬盘读写应该一致。
而真正的区别在于OutputStreamWriter的write()方法的调用次数
网上的东西很多是错的,包括这一篇,也不一定正确。