前面介绍了利用文件写入器和文件读取器来读写文件,因为FileWriter与FileReader读写的数据以字符为单位,所以这种读写文件的方式被称作“字符流I/O”,其中字母I代表输入Input,字母O代表输出Output。可是FileWriter的读操作并不高效,缘由在于FileWriter每次调用write方法都会直接写入文件,假如某项业务需要多次调用write方法,那么程序就会写入文件同样次数。因为写文件本质是写磁盘,磁盘的速度远不如内存,所以频繁地写文件必然严重降低程序的运行效率。为此Java又设计了缓存写入器BufferedWriter,它的write方法并不直接写入文件,而是先写入一块缓存,等到缓存写满了再将缓存上的数据写入文件。由于缓存空间位于内存之中,写入缓存等同访问内存,这样相当于把写磁盘动作替换成写内存动作,因此BufferedWriter的整体写文件性能要大大优于FileWriter。除此之外,BufferedWriter还新增了下列几个方法:
newLine:往文件末尾添加换行标记(Window系统是回车加换行)。当然实际上是先往缓存添加换行标记,并非直接往磁盘写入换行标记。
flush:立即将缓冲区中的数据写入磁盘。默认情况要等缓冲区满了才会写入磁盘,或者调用close方法关闭文件之时也会写入磁盘,但是有时程序猴急,一定要立即写入磁盘,此时就需调用flush方法强行写磁盘。
使用缓存写入器之前要先创建文件读取器对象,并获得父类Writer的实例,然后再据此创建缓存写入器对象。下面是通过缓存写入器把多行字符串写入文件的代码例子:

  private static String mSrcName = "D:/test/aad.txt";// 使用缓存字符流写入文件private static void writeBuffer() {String str1 = "白日依山尽,黄河入海流。";String str2 = "欲穷千里目,更上一层楼。";File file = new File(mSrcName); // 创建一个指定路径的文件对象// try(...)允许在圆括号内部拥有多个资源创建语句,语句之间以冒号分隔// 先创建文件写入器,再根据文件读取器创建缓存写入器try (Writer writer = new FileWriter(file);BufferedWriter bwriter = new BufferedWriter(writer);) {// FileWriter的每次write调用都会直接写入磁盘,不但效率低,性能也差。// BufferedWriter的每次write调用会先写入缓冲区,直到缓冲区满了才写入磁盘,// 缓冲区大小默认是8K,查看源码defaultCharBufferSize = 8192;// 资源释放的close方法再把缓冲区的剩余数据写入磁盘,// 或者中途调用flush方法也可提前将缓冲区的数据写入磁盘。bwriter.write(str1); // 往文件写入字符串bwriter.newLine(); // 另起一行,也就是在文件末尾添加换行标记(Window系统是回车加换行)bwriter.write(str2);  // 往文件写入字符串//bwriter.flush(); // 把缓冲区中的数据写入磁盘} catch (Exception e) {e.printStackTrace();}}

既然文件写入器有对应的缓存写入器,那么文件读取器也有对应的缓存读取器BufferedReader。BufferedReader的实现原理与它的兄弟BufferedWriter类似,另外BufferedReader比起文件读取器新增了如下方法:
readLine:从文件中读取一行数据。
mark:在当前位置做个标记。
reset:重置文件指针,令其回到上次标记的位置。也就是回到上次mark方法标记的文件位置。
lines:读取文件内容的所有行,返回的是Stream<String>流对象,之后便可按照流式处理来加工该字符串流。
若想使用缓存读取器,依然要先创建文件读取器,再根据其父类的读取器实例创建缓存读取器。下面是通过缓存读取器从文件中读取多行字符串的代码例子:

 // 使用缓存字符流读取文件private static void readBuffer() {File file = new File(mSrcName); // 创建一个指定路径的文件对象// try(...)允许在圆括号内部拥有多个资源创建语句,语句之间以冒号分隔// 先创建文件读取器,再根据文件读取器创建缓存读取器try (Reader reader = new FileReader(file);BufferedReader breader = new BufferedReader(reader);) {breader.mark((int) file.length()); // 做个标记for (int i=1; ; i++) {// FileReader只能一个字符一个字符地读,或者一次性读进字符数组。// BufferedReader还支持一行一行地读。String line = breader.readLine(); // 从文件中读出一行文字if (line == null) { // 读到了空指针,表示已经到了文件末尾break;}System.out.println("第"+i+"行的文字为:"+line);}breader.reset(); // 重置文件指针,令其回到上次标记的位置for (int i=1; ; i++) {String line = breader.readLine(); // 从文件中读出一行文字if (line == null) { // 读到了空指针,表示已经到了文件末尾break;}System.out.println("又读了一遍 第"+i+"行的文字为:"+line);}//breader.lines(); // 返回Stream<String>对象,之后可按照流式处理来加工该字符串流} catch (Exception e) {e.printStackTrace();}}

注意到以上代码BufferedWriter和BufferedReader的创建语句都位于try后面的圆括号之中,这是因为Writer与Reader两大家族统统实现了AutoCloseable接口,所以由它们繁衍而来的所有子类都具备自动释放资源的功能。另外,try语句支持同时管理多个资源类,只要它们的对象创建语句以冒号隔开,程序在运行时即可自动回收相关的资源。
结合运用读操作和写操作,可以实现文件复制的功能,无非是一边从源文件中读出数据,另一边紧接着往目标文件写入数据。采用缓存读取器和缓存写入器逐行复制的话,具体的文件复制代码示例如下:

    private static String mSrcName = "D:/test/aad.txt";private static String mDestName = "D:/test/aad_copy.txt";// 通过缓存字符流逐行复制文件private static void copyFile() {File src = new File(mSrcName); // 创建一个指定路径的源文件对象File dest = new File(mDestName); // 创建一个指定路径的目标文件对象// try(...)允许在圆括号内部拥有多个资源创建语句,语句之间以冒号分隔// 分别创建源文件的缓存读取器,以及目标文件的缓存写入器try (BufferedReader breader = new BufferedReader(new FileReader(src));BufferedWriter bwriter = new BufferedWriter(new FileWriter(dest));) {for (int i=0; ; i++) {String line = breader.readLine(); // 从文件中读出一行文字if (line == null) { // 读到了空指针,表示已经到了文件末尾break;}if (i != 0) { // 第一行开头不用换行bwriter.newLine(); // 另起一行,也就是在文件末尾添加换行标记}bwriter.write(line); // 往文件写入字符串}} catch (Exception e) {e.printStackTrace();}System.out.println("文件复制完成,源文件大小="+src.length()+",新文件大小="+dest.length());}

或者也可逐个字符来复制文件,此时BufferedReader每次调用的read方法只返回整型数表示一个字符,并且BufferedWriter每次调用的write方法也只写入该字符对应的整型数。通过依次遍历源文件的所有字符,同时往目标文件依次写入这些字符,从而完成逐个字符复制文件的操作流程。下面是采取逐字符复制文件的代码例子:

   // 通过缓存字符流逐个字符复制文件private static void copyFileByInt() {File src = new File(mSrcName); // 创建一个指定路径的源文件对象File dest = new File(mDestName); // 创建一个指定路径的目标文件对象// try(...)允许在圆括号内部拥有多个资源创建语句,语句之间以冒号分隔// 分别创建源文件的缓存读取器,以及目标文件的缓存写入器try (BufferedReader breader = new BufferedReader(new FileReader(src));BufferedWriter bwriter = new BufferedWriter(new FileWriter(dest));) {while (true) { // 开始遍历文件中的所有字符int temp = breader.read(); // 从源文件中读出一个字符if (temp == -1) { // read方法返回-1表示已经读到了文件末尾break;}bwriter.write(temp); // 往目标文件写入一个字符}} catch (Exception e) {e.printStackTrace();}System.out.println("文件复制完成,源文件大小="+src.length()+",新文件大小="+dest.length());}

需要注意的是,使用字符流复制文件只有逐行复制和逐字符复制两种方式,不可采取整个读到字符数组再整个写入字符数组的方式。之所以不能通过字符数组复制文件,是因为中文跟英文不一样,一个汉字会占用多个字节(GBK编码的每个汉字占用两个字节,UTF8编码的每个汉字占用三个字节)。若要把文件内容读到字符数组,势必先得知晓该数组的长度,可是调用文件对象的length方法只能得到该文件的字节长度,并非字符长度。譬如“白日依山尽”这个字符串在内存中的字符数组长度为5,写到UTF8编码的文件之后,文件大小是5*3=15字节;接着想把文件内容读到字符数组,然而15字节的文件天晓得它有几个字符,可能有5个UTF8编码的中文字符,也可能有15个英文字符,也可能有5个GBK编码的中文字符加5个英文字符共10个字符,总之你根本想不到该分配多大的字符数组。既然确定不了待读取的字符数组长度,就无法一字不差地复制文件内容了。

更多Java技术文章参见《Java开发笔记(序)章节目录》

转载于:https://www.cnblogs.com/pinlantu/p/10726494.html

Java开发笔记(八十六)通过缓冲区读写文件相关推荐

  1. 【Visual C++】游戏开发笔记四十六 浅墨DirectX教程十四 模板测试与镜面特效专场

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处.   文章链接: http://blog.csdn.net/zhmxy555/article/details/8632184 作者:毛星云( ...

  2. 【Visual C++】游戏开发笔记三十六 浅墨DirectX提高班之四 顶点缓存的逆袭

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处. 文章链接: http://blog.csdn.net/zhmxy555/article/details/8276363 作者:毛星云(浅墨 ...

  3. Android开发笔记(十六)秋千摇摆动画SwingAnimation

    上节博主介绍了AlphaAnimation和淡入淡出动画的使用,其实AlphaAnimation只是四种补间动画中的一种.那么为了加深对其他补间动画的理解,我想说说旋转动画RotateAnimatio ...

  4. 【Visual C++】游戏开发笔记二十六 DirectX 11各组件的介绍 第一个DirectX 11 Demo的创建

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 本系列文 ...

  5. 【Visual C++】游戏开发笔记二十六 DirectX 11各组件的介绍第一个DirectX 11 Demo的创建

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处. http://blog.csdn.net/zhmxy555/article/details/7688515 作者:毛星云    邮箱: h ...

  6. 【Visual C++】游戏开发笔记二十六 DirectX 11各组件的介绍第一个DirectX 11 Demo的创建...

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处. http://blog.csdn.net/zhmxy555/article/details/7688515 作者:毛星云 邮箱: happ ...

  7. 易课寄在线购课系统开发笔记(十六)--完成内容服务系统的工程搭建

    首页动态展示分析 内容信息要从数据库中获得. 动态展示分析 内容需要进行分类. 分类下有子分类,需要动态管理. 分类下有内容列表. 单点的内容信息: 有图片 有链接 有标题 有价格 包含大文本类型,可 ...

  8. Java学习笔记(十六)—— 开发个小项目(GoBang4.0)

    接十二,今天重点搞定简单基础版本的AI下棋. 画个大纲(跟随慢慢开发过程不断完善) 1.用户 两个用户对战  一黑一白 用户可以是人,也可以是AI.对战模式支持人人,人机,机机. 属性 本次比赛执棋颜 ...

  9. Java学习笔记二十六:Java多态中的引用类型转换

    Java多态中的引用类型转换 引用类型转换: 1.向上类型转换(隐式/自动类型转换),是小类型到大类型的转换: 2.向下类型转换(强制类型转换),是大类型到小类型的转换: 3.instanceof运算 ...

  10. java中的多态与继承_【Java学习笔记之十六】浅谈Java中的继承与多态

    1.  什么是继承,继承的特点? 子类继承父类的特征和行为,使得子类具有父类的各种属性和方法.或子类从父类继承方法,使得子类具有父类相同的行为. 特点:在继承关系中,父类更通用.子类更具体.父类具有更 ...

最新文章

  1. Actionscript3.0动画编程中的几种特效举例
  2. android 的命令行工具(dos命令)
  3. POJ - 4045 Power Station(树形dp/树的重心)
  4. 在java中使用关键字导入包_java中import关键字的使用方法
  5. SSL 多线程通信 linux openSSL C API编程
  6. 太极发送卡片软件_QQ卡片式消息一键发送app
  7. ElasticSearch配置说明
  8. Linux_CentOS-服务器搭建 五 补充
  9. aop springboot 传入参数_springboot用aop做参数校验
  10. backtrack5 oracle,BackTrack5(BT5)硬盘安装
  11. 关于EFS加密原理及破解浅谈
  12. LaTex多张子图并排排列方法
  13. 三星Q950T全景声回音壁测试心得
  14. 在控制面板找不到程序的情况下,卸载流氓软件
  15. 猿人学web端爬虫攻防大赛赛题解析_第九题:js混淆-动态cookie2
  16. java BigDecimal比较大小
  17. 谴责那些没有良知的人
  18. 计算机专业大创要求,计算机学院举行2018年度“大创项目”研究工作推进会
  19. 新机器通过U盘安装WIN7原版时提示缺少驱动的解决方法
  20. jfinal hutool工具 excelUtil ZipUtil实现导出excel并压缩文件

热门文章

  1. intellij idea rearrange code
  2. 数据库外连接和内连接详解
  3. 一个程序员的创业失败教训【转自CSDN】
  4. Swift 语言快速入门
  5. python进阶03UnboundLocalError和NameError错误
  6. java 怎么接收xml_Java如何提取完整的XML块
  7. LinuxCentos7 防火墙开放端口,查看状态,查看开放端口
  8. oracle update范例,oracle 12c单范例数据库打12.1.0.2.4补丁记录
  9. 两个单片机iic通讯程序_PIC单片机之DS1302时钟
  10. 并注册烧写钩子 获取启动介质类型_PyTorch中对张量登记注册反向传播的钩子函数,并展示调用顺序...