缓冲流

缓冲流

学习了基本的一些流,作为IO流的入门,今天我们要见识一些更强大的流。比如能够高效读写的缓冲流,能够转换编码的转换流,能够持久化存储对象的序列化流等等。这些功能更为强大的流,都是在基本的流对象基础之上创建而来的,就像穿上铠甲的武士一样,相当于是对基本流对象的一种增强。

1.1 概述

缓冲流,也叫高效流,是对4个基本的FileXxx 流的增强,所以也是4个流,按照数据类型分类:

  • 字节缓冲流BufferedInputStreamBufferedOutputStream
  • 字符缓冲流BufferedReaderBufferedWriter

缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。

1.2 字节缓冲

字节缓冲输出流

java.io.BufferedOutputStream extends OutputStream

BufferedOutputStream:字节缓冲输出流

继承自父类的共性方法:

  • public void close():关闭此输出流并释放与此资源相关的任何系统资源
  • void flush() 刷新此输出流并强制写出所有缓冲的输出字节
  • void write(byte[] b)b.length 个字节从指定的 byte 数组写入此输出流
  • void write(byte[] b, int off, int len) 将指定 byte 数组中从偏移量off开始的 len个字节写入此输出流。
  • abstract void write(int b) 将指定的字节写入此输出流

构造方法

  • BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
  • BufferedOutputStream(OutputStream out, int size) 创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流。

参数:

  • outputStream out:字节输出流
    • 我们可以传递FileOutputStream,缓冲流会给FileoutputStream增加一个缓冲区,提高FileOutputStream的写入效率
  • int size:指定缓冲流内部缓冲区的大小,不指定默认

使用步骤(重点)

  1. 创建FileOutputStream对象,构造方法中绑定要输入的目的地
  2. 创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象,提高FileOutputStream对象效率
  3. 使用BufferedOutputStream对象中的方法write,把数据写入到内部缓冲区
  4. 使用BufferedOutputStream对象中的方法flush,把内部缓冲区中的数据,刷新到文件中
  5. 释放资源(会先调用flush方法刷新数据,第4部可以省略)

示例代码:

 public static void main(String[] args) throws IOException {
        //  1. 创建FileOutputStream对象,构造方法中绑定要输入的目的地
        FileOutputStream fos = new FileOutputStream("D:\Test\g.txt");
        //  2. 创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象,提高FileOutputStream对象效率
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        // 3. 使用BufferedOutputStream对象中的方法write,把数据写入到内部缓冲区
        bos.write("把数据写到缓冲区".getBytes());
        // getBytes():把字符串转换为字节数组
        //  4. 使用BufferedOutputStream对象中的方法flush,把内部缓冲区中的数据,刷新到文件中
        bos.flush();
        //  5. 释放资源(会先调用flush方法刷新数据,第4部可以省略)
         bos.close();
    }

字节缓冲输入流

BufferedInputStream:字节缓冲输入流

java.io.BufferedInputStream extends InputStream

继承父类的成员方法

  • int read() 从输入流中读取数据的下一个字节
  • int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区b中
  • void close() 关闭此输入流并释放与该流相关联的所有系统资源

构造方法

  • BufferedInputStream(InputStream in) 创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用
  • BufferedInputStream(InputStream in int size) 创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用
  • 参数:
    • InputStream in:字节输入流
      • 我们可以传递FileInputSrteam,缓冲流会给FileInputStream增加一个缓冲区,提高FileInputStream的读取效率
    • int size :指定缓冲流内部缓冲区的大小,不指定默认

使用步骤(重点)

  1. 创建FileInputStream对象,构造方法中绑定要读取的数据源
  2. 创建BuffreedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象的读取效率
  3. 使用BufferedInputStream对象中的方法read,读取文件
  4. 释放资源

示例代码

public static void main(String[] args) throws IOException {
        // 1. 创建FileInputStream对象,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("D:\Test\g.txt");
        // 2. 创建BuffreedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象的读取效率
        BufferedInputStream bis = new BufferedInputStream(fis);
        // 3. 使用BufferedInputStream对象中的方法read,读取文件

        // int read() 从输入流中读取数据的下一个字节
        /*int len = 0;
        // 记录读取到的有效字节个数
        while ((len = bis.read())!=-1){
            System.out.println(len);
        }*/
        // int read(byte[] b)  从输入流中读取一定数量的字节,并将其存储在缓冲区b中
        // 记录每次读取到的有效字节
        int len = 0;
        // 存储每次读取到的数据
        byte[] bytes = new byte[1024];
        while ((len = bis.read(bytes))!=-1){
            System.out.println(new String(bytes,0,len));
        }
                 bis.close();

    }

效率测试

查询API,缓冲流读写方法与基本的流是一致的,我们通过复制文件,测试它的效率。

基本流

代码如下

public static void main(String[] args) throws IOException {
        long s = System.currentTimeMillis();
        // 1. 创建一个字节输入流对象,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("c:\Test\1.jpg");
        // 2. 创建一个字节输出流对象,构造方法中绑定要写入的目的地
        FileOutputStream fos = new FileOutputStream("d:\Test\1.jpg");
        // 3. 使用字节输入流对象中的方法read读取文件
        // 一次读取一个字节写入一个字节的方式
        /*int len = 0;
        while ((len = fis.read())!= -1){
            // 4. 使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
            fos.write(len);
        }*/

        // 使用数组缓冲区读取多个字节,写入多个字节
        byte[] bytes = new byte[1024];
        // 3.使用字节输入流对象中的方法read读取文件
        int len = 0;
        while ((len = fis.read(bytes))!=-1){
            fos.write(bytes,0,len);
        }
        // 每次读取字节的个数

        // 5. 释放资源(先关写的,后关读的,如果写完了,肯定读取完毕了)
        fos.close();
        fis.close();
        long e = System.currentTimeMillis();
        System.out.println("程序运行时间:"+(e-s)+"毫秒");

    }
}
// 运行结果
文件大小:2,629,730 字节
使用字节输入输出流耗时:14179毫秒
添加个数组  使用字节输入输出流耗时:74毫秒

缓冲流

代码如下

public static void main(String[] args) throws IOException {
        long s = System.currentTimeMillis();
        // 创建字节缓冲输入流对象
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("c:\Test\1.jpg"));
        // 创建字节缓冲输出流对象
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("d:\Test\1.jpg"));
        /*// 使用字节输入流的read方法读取文件
        int len = 0;
        while ((len = bis.read())!=-1){
            bos.write(len);
        }*/

        // 使用数组方法
        byte[] bytes = new byte[1024];
        int len = 0;
        while ((len = bis.read(bytes))!=-1){
            bos.write(bytes,0,len);
        }
        bos.close();
        bis.close();
        long t = System.currentTimeMillis();
        System.out.println("复制耗费的时间:"+(t-s)+"毫秒");
    }
}
// 运行结果
 文件大小:2,629,730 字节
 使用字节缓冲输入输出流耗时:155毫秒
 添加个数组  使用字节缓冲输入输出流耗时:8毫秒

1.3 字符缓冲流

构造方法

  • public BufferedReader(Reader in) :创建一个 新的缓冲输入流。
  • public BufferedWriter(Writer out): 创建一个新的缓冲输出流。

构造举例,代码如下:

// 创建字符缓冲输入流
BufferedReader br = new BufferedReader(new FileReader("br.txt"));
// 创建字符缓冲输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));

特有方法

字符缓冲流的基本方法与普通字符流调用方式一致,不再阐述,我们来看它们具备的特有方法。

  • BufferedReader:public String readLine(): 读一行文字。
  • BufferedWriter:public void newLine(): 写一行行分隔符,由系统属性定义符号。

readLine方法演示,代码如下:

public class BufferedReaderDemo {
    public static void main(String[] args) throws IOException {
      	 // 创建流对象
        BufferedReader br = new BufferedReader(new FileReader("in.txt"));
		// 定义字符串,保存读取的一行文字
        String line  = null;
      	// 循环读取,读取到最后返回null
        while ((line = br.readLine())!=null) {
            System.out.print(line);
            System.out.println("------");
        }
		// 释放资源
        br.close();
    }
}

newLine方法演示,代码如下:

public class BufferedWriterDemo throws IOException {
    public static void main(String[] args) throws IOException  {
      	// 创建流对象
		BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt"));
      	// 写出数据
        bw.write("黑马");
      	// 写出换行
        bw.newLine();
        bw.write("程序");
        bw.newLine();
        bw.write("员");
        bw.newLine();
		// 释放资源
        bw.close();
    }
}
输出效果:
黑马
程序
员

1.4 练习:文本排序

请将文本信息恢复顺序。

3.侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必得裨补阙漏,有所广益。
8.愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。
4.将军向宠,性行淑均,晓畅军事,试用之于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。
2.宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。
1.先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。
9.今当远离,临表涕零,不知所言。
6.臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。
7.先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐付托不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
5.亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。

案例分析

  1. 逐行读取文本信息。
  2. 解析文本信息到集合中。
  3. 遍历集合,按顺序,写出文本信息。

案例实现

public class BufferedTest {
    public static void main(String[] args) throws IOException {
        // 创建map集合,保存文本数据,键为序号,值为文字
        HashMap<String, String> lineMap = new HashMap<>();

        // 创建流对象
        BufferedReader br = new BufferedReader(new FileReader("in.txt"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt"));

        // 读取数据
        String line  = null;
        while ((line = br.readLine())!=null) {
            // 解析文本
            String[] split = line.split("\.");
            // 保存到集合
            lineMap.put(split[0],split[1]);
        }
        // 释放资源
        br.close();

        // 遍历map集合
        for (int i = 1; i <= lineMap.size(); i++) {
            String key = String.valueOf(i);
            // 获取map中文本
            String value = lineMap.get(key);
          	// 写出拼接文本
            bw.write(key+"."+value);
          	// 写出换行
            bw.newLine();
        }
		// 释放资源
        bw.close();
    }
}
原文地址:https://www.cnblogs.com/anke-z/p/12697066.html