IO中的缓冲流理解

1,365 阅读8分钟

缓冲流

基本介绍

缓冲流可以提高字节流和字符流的读写数据的性能

  • BufferedInputStream 是 Java 编程语言中的一个类,它为输入流提供缓冲。它用于从源(如文件或网络连接)读取数据,并将数据存储在缓冲区中。这可以通过减少所需的单个读取操作的数量来提高输入操作的效率。

  • BufferedOutputStream 是 Java 编程语言中的一个类,它为输出流提供缓冲。它用于将数据写入目的地,例如文件或网络连接,并将数据存储在缓冲区中。这可以通过减少所需的单个写入操作的数量来提高输出操作的效率。

  • BufferedReader 是 Java 编程语言中的一个类,它为读取器流提供缓冲。它用于从源读取文本数据并将数据存储在缓冲区中。可用于提高文本输入操作的效率,支持字符编码方案的使用。

  • BufferedWriter 是 Java 编程语言中的一个类,它为编写器流提供缓冲。它用于将文本数据写入目标并将数据存储在缓冲区中。可用于提高文本输出操作的效率,支持字符编码方案的使用。

总体而言,这些类用于为 Java 中的输入/输出流提供缓冲,提高输入/输出操作的效率,支持文本数据使用字符编码方案。

字节缓冲输入

字节缓冲输入流:BufferedInputStream

作用:可以把低级的字节输入流包装成一个高级的缓冲字节输入流管道,提高字节输入流读数据的性能

构造器:public BufferedInputStream(InputStream in)

原理:缓冲字节输入流管道自带了一个 8KB 的缓冲池,每次可以直接借用操作系统的功能最多提取 8KB 的数据到缓冲池中去,以后我们直接从缓冲池读取数据,所以性能较好

public class BufferedInputStreamDemo01 {
    public static void main(String[] args) throws Exception {
        // 1.定义一个低级的字节输入流与源文件接通
        InputStream is = new FileInputStream("Demo/src/test.txt");
        // 2.把低级的字节输入流包装成一个高级的缓冲字节输入流。
        BufferInputStream bis = new BufferInputStream(is);
        // 3.定义一个字节数组按照循环读取。
        byte[] buffer = new byte[1024];
        int len;
        while((len = bis.read(buffer)) != -1){
            String rs = new String(buffer, 0 , len);
            System.out.print(rs);
        }
    }
}
字节缓冲输出

字节缓冲输出流:BufferedOutputStream

作用:可以把低级的字节输出流包装成一个高级的缓冲字节输出流,从而提高写数据的性能

构造器:public BufferedOutputStream(OutputStream os)

原理:缓冲字节输出流自带了 8KB 缓冲池,数据就直接写入到缓冲池中去,性能提高了

public class BufferedOutputStreamDemo02 {
    public static void main(String[] args) throws Exception {
        // 1.写一个原始的字节输出流
        OutputStream os = new FileOutputStream("Demo/src/test.txt");
        // 2.把低级的字节输出流包装成一个高级的缓冲字节输出流
        BufferedOutputStream bos =  new BufferedOutputStream(os);
        // 3.写数据出去
        bos.write('a');
        bos.write(100);
        bos.write("我爱中国".getBytes());
        bos.close();
    }
}
字节流性能

利用字节流的复制统计各种写法形式下缓冲流的性能执行情况

复制流:

  • 使用低级的字节流按照一个一个字节的形式复制文件
  • 使用低级的字节流按照一个一个字节数组的形式复制文件
  • 使用高级的缓冲字节流按照一个一个字节的形式复制文件
  • 使用高级的缓冲字节流按照一个一个字节数组的形式复制文件

高级的缓冲字节流按照一个一个字节数组的形式复制文件,性能最高,建议使用


字符缓冲输入

字符缓冲输入流:BufferedReader

作用:字符缓冲输入流把字符输入流包装成高级的缓冲字符输入流,可以提高字符输入流读数据的性能。

构造器:public BufferedReader(Reader reader)

原理:缓冲字符输入流默认会有一个 8K 的字符缓冲池,可以提高读字符的性能

按照行读取数据的功能:public String readLine() 读取一行数据返回,读取完毕返回 null

public static void main(String[] args) throws Exception {
    // 1.定义一个原始的字符输入流读取源文件
    Reader fr = new FileReader("Demo/src/test.txt");
    // 2.把低级的字符输入流管道包装成一个高级的缓冲字符输入流管道
    BufferedReader br = new BufferedReader(fr);
    // 定义一个字符串变量存储每行数据
    String line;
    while((line = br.readLine()) != null){
        System.out.println(line);
    }
    br.close();
    //淘汰数组循环读取
    //char[] buffer = new char[1024];
    //int len;
    //while((len = br.read(buffer)) != -1){
    //System.out.println(new String(buffer , 0 , len));
}

字符缓冲输出

符缓冲输出流:BufferedWriter

作用:把低级的字符输出流包装成一个高级的缓冲字符输出流,提高写字符数据的性能。

构造器:public BufferedWriter(Writer writer)

原理:高级的字符缓冲输出流多了一个 8K 的字符缓冲池,写数据性能极大提高了

字符缓冲输出流多了一个换行的特有功能:public void newLine() 新建一行

public static void main(String[] args) throws Exception {
    Writer fw = new FileWriter("Demo/src/test.txt",true);//追加
    BufferedWriter bw = new BufferedWriter(fw);
    
    bw.write("我爱学习Java");
    bw.newLine();//换行
    bw.close();
}

高效原因

字符型缓冲流高效的原因:(空间换时间)

  • BufferedReader:每次调用 read 方法,只有第一次从磁盘中读取了 8192(8k)个字符,存储到该类型对象的缓冲区数组中,将其中一个返回给调用者,再次调用 read 方法时,就不需要访问磁盘,直接从缓冲区中拿出一个数据即可,提升了效率
  • BufferedWriter:每次调用 write 方法,不会直接将字符刷新到文件中,而是存储到字符数组中,等字符数组写满了,才一次性刷新到文件中,减少了和磁盘交互的次数,提升了效率

字节型缓冲流高效的原因:

  • BufferedInputStream:在该类型中准备了一个数组,存储字节信息,当外界调用 read() 方法想获取一个字节的时候,该对象从文件中一次性读取了 8192 个字节到数组中,只返回了第一个字节给调用者。将来调用者再次调用 read 方法时,当前对象就不需要再次访问磁盘,只需要从数组中取出一个字节返回给调用者即可,由于读取的是数组,所以速度非常快。当 8192 个字节全都读取完成之后,再需要读取一个字节,就得让该对象到文件中读取下一个 8192 个字节
  • BufferedOutputStream:在该类型中准备了一个数组,存储字节信息,当外界调用 write 方法想写出一个字节的时候,该对象直接将这个字节存储到了自己的数组中,而不刷新到文件中。一直到该数组所有 8192 个位置全都占满,该对象才把这个数组中的所有数据一次性写出到目标文件中。如果最后一次循环没有将数组写满,最终在关闭流对象的时候,也会将该数组中的数据刷新到文件中。

注意:字节流和字符流,都是装满时自动写出,或者没满时手动 flush 写出,或 close 时刷新写出

转换流

乱码问题

字符流读取:

代码编码            文件编码         中文情况。
UTF-8              UTF-8           不乱码!
GBK                GBK             不乱码!
UTF-8              GBK             乱码!
  • 如果代码编码和读取的文件编码一致,字符流读取的时候不会乱码
  • 如果代码编码和读取的文件编码不一致,字符流读取的时候会乱码
字符输入

字符输入转换流:InputStreamReader

作用:解决字符流读取不同编码的乱码问题,把原始的字节流按照默认的编码或指定的编码转换成字符输入流

构造器:

  • public InputStreamReader(InputStream is):使用当前代码默认编码 UTF-8 转换成字符流
  • public InputStreamReader(InputStream is, String charset):指定编码把字节流转换成字符流
public class InputStreamReaderDemo{
    public static void main(String[] args) throws Exception {
        // 1.提取GBK文件的原始字节流
        InputStream is = new FileInputStream("D:\seazean\Netty.txt");
        // 2.把原始字节输入流通过转换流,转换成 字符输入转换流InputStreamReader
        InputStreamReader isr = new InputStreamReader(is, "GBK"); 
        // 3.包装成缓冲流
        BufferedReader br = new BufferedReader(isr);
        //循环读取
        String line;
        while((line = br.readLine()) != null){
            System.out.println(line);
        }
    }
}
字符输出

字符输出转换流:OutputStreamWriter

作用:可以指定编码把字节输出流转换成字符输出流,可以指定写出去的字符的编码

构造器:

  • public OutputStreamWriter(OutputStream os):用默认编码 UTF-8 把字节输出流转换成字符输出流
  • public OutputStreamWriter(OutputStream os, String charset):指定编码把字节输出流转换成
OutputStream os = new FileOutputStream("/usr/local");
OutputStreamWriter osw = new OutputStreamWriter(os,"GBK");
osw.write("测试i");   
osw.close();

本文正在参加「金石计划 . 瓜分6万现金大奖」