> 文档中心 > 白话Java缓冲流(BufferedInputStream,BufferedOutputStream)

白话Java缓冲流(BufferedInputStream,BufferedOutputStream)

白话缓冲流(BufferedInputStream,BufferedOutputStream)

  • 1. 什么是流
    • 1.1 读写(复制)文件案例
  • 2. 什么是缓冲流
    • 2.1 缓冲流的作用。
      • 2.1.1 缓冲流的逻辑。
      • 2.1.2 输入缓冲流案例:
      • 2.1.3 输出缓冲流案例(复制文件):

看了整个CSDN,关于缓冲流的文章基本都是模糊不清,晦涩难懂或者复制粘贴的。
这里做一个简单的分享,以免面试的时候被问到那可就阴沟里翻船了。

🍅你的点赞是我分享的动力🍅

其实大多数Java开发人员在工作中很少会接触的IO流。可面试偏偏问得又比较多,所以我们在闲暇时间还是要把基础好好复习一下。

1. 什么是流

流是一个抽象的概念。在我们Java程序中要去读取一个文件的内容,那么读取的这个动作(通道)就叫做输入流;(下图1)
这时候如果我要Java程序要把一些数据输出到电脑本地形成一个文件,那么输出的这个动作(通道)就叫做输出流。(下图2)
在这里插入图片描述

1.1 读写(复制)文件案例

我们这里用java代码简单演示一下输入流和输出流的操作(输入流读取文件,输出流创建文件):

    public  static void test1 ()throws IOException{    1.初始化主备读取的文件对象。 File file = new File("C:\\Users\\Administrator\\Desktop\\博客.txt");     2.初始化改文件的输入流对象。     InputStream inputStream = new FileInputStream(file);     3.初始化新的文件的输出流对象。     OutputStream outputStream = new FileOutputStream("C:\\Users\\Administrator\\Desktop\\博客01.txt"); 4.定义byte数组,用于保存输入流读取到的数据,我们这里定义10长度,说明一次读取10位数据。 byte[] bytes = new byte[10];     5.表示已经读取了多少个字节,如果是 -1,表示已经读取到文件的末尾。 int len = -1; 6.inputStream.read(bytes)方法返回int数据,表示本次读取到的字节数。-1的话表示文件已经被读完。 while ((len = inputStream.read(bytes)) != -1){ 7.将 bytes数组中从 0 开始,长度为 len 的数据输出到 博客01.txt 文件中     outputStream.write(bytes,0,len); } inputStream.close(); outputStream.close();    }

由于本文主要讨论缓冲流,这里对基础的IO流就简单带过。

2. 什么是缓冲流

在了解缓冲流之前,我们先要知道流的分类。 从功能上分类,流分为节点流包装流

  1. 节点流:简单来说就是普通的输入流输出流,如上面案例中的InputStream/OutputStream 。
  2. 包装流:包装流、包装流,顾名思义就是将节点流给包装起来的一种流。而我们将要说的缓冲流就是包装流。

我们先看看缓冲流在Java中是怎么定义的。
白话Java缓冲流(BufferedInputStream,BufferedOutputStream)
嗯 ?飘红了,我们去源码中看看构造函数:

    public BufferedInputStream(InputStream in) { this(in, DEFAULT_BUFFER_SIZE);    }

由此我们得到一个定理:

缓冲流的定义需要一个已经实例化的节点流作为参数。

2.1 缓冲流的作用。

缓冲流:目的是缓存作用,加快读取和写入数据的速度。

程序频繁地操作一个资源(如文件),则性能会很低,此时为了提升性能,就可以将一部分数据暂时读入到内存的一块区域中,以后直接从此区域中读取数据即可,因为读内存速度比较快,这样提高性能。在IO中引入缓冲区,主要是提高流的读写效率。

说起来简单,理解起来就很费劲。怎么就加快了读取和写入的速度了呢?

在这里插入图片描述

回忆一下目录1.1中的复制文件案例。

4.定义byte数组,用于保存输入流读取到的数据,我们这里定义10长度,说明一次读取10位数据。 byte[] bytes = new byte[10];     5.表示已经读取了多少个字节,如果是 -1,表示已经读取到文件的末尾。 int len = -1; 6.inputStream.read(bytes)方法返回int数据,表示本次读取到的字节数。-1的话表示文件已经被读完。 while ((len = inputStream.read(bytes)) != -1){ 7.将 bytes数组中从 0 开始,长度为 len 的数据输出到 博客01.txt 文件中     outputStream.write(bytes,0,len); }
  1. 我们定义了一个长度为10的数组用来保存每次读取到的字节。
  2. 循环读取文件,每次读取10个字节,并将这10个字节输出到新的文件中。直到文件被读完。

好,整个逻辑没什么问题,功能也是完成了,是不是就大功告成了呢?
如果你觉得是,那恭喜你,你是一个合格的增删改查工程师!

很显然,还有优化的空间。
我们知道,IO操作时非常浪费性能的一件事,不管是读取文件还是访问数据库。
在高性能的开发中我们都会尽量避免直接访问数据库,所以我们平时会引入缓存这个概念(Redis等)。为什么呢?因为对缓存的读写比IO读写(磁盘读写)性能提升不是一点半点;那IO操作是不是也可以有缓存这个东西呢

聪明的同学看到这应该发现了,上面的代码虽然没什么问题,但可能会造成十分严重的性能问题

  1. 我们定义了一个长度为10的数组用来保存每次读取到的字节。
  2. 循环读取文件,每次读取10个字节,并将这10个字节输出到新的文件中。直到文件被读完。
  3. 如果文件有1000个字节呢?我要读100次,写100次。如果文件有10000个字节呢?我要读1000次,写1000次。然而每读写一次都是直接从磁盘读写的IO操作,性能极差。
  4. 这时候有人说了:“我可以一次性多读一点啊,10个字节太小了,我要读1024或者更多,这样IO操作不九就减少了吗?!”。
  5. 这确实是一个办法,但我们要记住,程序离不开业务,从实际的业务场景来看,我们很多时候需要去指定每次读取的字节数,而且一般不会是特别大数量的。因为读取之后往往会对读取到的数据进行业务逻辑操作。
  6. 这个时候缓冲流的作用就体现出来了。

2.1.1 缓冲流的逻辑。

看一段代码:

 File file = new File("C:\\Users\\Administrator\\Desktop\\博客.txt"); InputStream inputStream = new FileInputStream(file); 1.为输入流inputStream 定义一个它的包装流:bufferedInputStream  BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);

好,我们去看看这个构造函数做了什么事情:
白话Java缓冲流(BufferedInputStream,BufferedOutputStream)
白话Java缓冲流(BufferedInputStream,BufferedOutputStream)

DEFAULT_BUFFER_SIZE:默认缓存大小(可自定义)。
白话Java缓冲流(BufferedInputStream,BufferedOutputStream)

可以看到,构造函数最后为我们创建了一个长度为8192的数组。数组名是:buf

这个buf数组就是我们一直以来心心念念的缓存

2.1.2 输入缓冲流案例:

 File file = new File("C:\\Users\\Administrator\\Desktop\\博客.txt"); InputStream inputStream = new FileInputStream(file); BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream); byte[] bytes = new byte[10]; int len = -1; while ((len = bufferedInputStream.read(bytes)) != -1) {     System.out.println("读取到的数据:"+new String(bytes)); }
  1. 我们创建了一个bufferedInputStream 输入缓冲流
  2. 这时候bufferedInputStream 会直接读取文件,将文件中的8192个字节先读取出来,保存在buf数组中。
  3. 好,开始10个字节10个字节得读取。这里得读取就不是从磁盘文件中读取了,而是从内存中的buf数组中读取,性能极佳。

2.1.3 输出缓冲流案例(复制文件):

我们现在利用缓冲流解决了读的问题。那写的时候怎么办呢?还是10个字节10个字节地去写入磁盘吗?

 File file = new File("C:\\Users\\Administrator\\Desktop\\博客.txt"); InputStream inputStream = new FileInputStream(file); OutputStream outputStream = new FileOutputStream("C:\\Users\\Administrator\\Desktop\\博客01.txt");1.创建输入缓冲流 BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream); 2.创建输出缓冲流  BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream); byte[] bytes = new byte[10]; int len = -1; while ((len = bufferedInputStream.read(bytes)) != -1){3.利用缓冲流,将数据写入到缓存中,待缓存到达了8129后,再一次性写入磁盘。     bufferedOutputStream.write(bytes,0,len); } 4.清空缓存区,以免漏掉缓存区中地数据,将里面地数据强制输出。  bufferedOutputStream.flush();  bufferedInputStream.close(); bufferedOutputStream.close();

51银饰网