白话Java缓冲流(BufferedInputStream,BufferedOutputStream)

看了整个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中是怎么定义的。
在这里插入图片描述
嗯 ?飘红了,我们去源码中看看构造函数:

    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);

好,我们去看看这个构造函数做了什么事情:
在这里插入图片描述
在这里插入图片描述

DEFAULT_BUFFER_SIZE:默认缓存大小(可自定义)。
在这里插入图片描述

可以看到,构造函数最后为我们创建了一个长度为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();
  • 10
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

易柏州Innovation

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值