Java中IO流基本介绍(4)——BuffereInputStream和BuffereOutputStream

1 基本概括

2 主要介绍

2.1 BuffereInputStream中的fill方法

源码解读

private void fill() throws IOException {
    byte[] buffer = getBufIfOpen();
    if (markpos < 0) {  
      /*如果不存在标记位置(即没有需要进行reset的位置需求) 
        则可以进行大胆地直接重置pos标识下一可读取位置,但是这样 
        不是会读取到以前的旧数据吗?不用担心,在后面的代码里☆会实现输入流的新  
        数据填充*/
    pos = 0;          
}else if (pos >= buffer.length){
    /* 位置大于缓冲区长度,这里表示已经没有可用空间了 */
    if (markpos > 0) {     
         /* 表示存在mark位置,则要对mark位置到pos位置的数据予以保留, 
            以确保后面如果调用reset()重新从mark位置读取会取得成功*/
        int sz = pos - markpos;
        /*该实现是通过将缓冲区域中markpos至pos部分的移至缓冲区头部实现*/
        System.arraycopy(buffer, markpos, buffer, 0, sz);
        pos = sz;
        markpos = 0;
    } else if (buffer.length >= marklimit) {
        /* 如果缓冲区已经足够大,可以容纳marklimit,则直接重置*/
        markpos = -1;
        pos = 0;/* 丢弃所有的缓冲区内容 */
    } else {
        /* 如果缓冲区还能增长的空间,则进行缓冲区扩容*/
        int nsz = pos * 2;
        /*新的缓冲区大小设置成满足最大标记极限即可*/
        if (nsz > marklimit)
            nsz = marklimit;
        byte nbuf[] = new byte[nsz];
        //将原来的较小的缓冲内容COPY至增容的新缓冲区中  
        System.arraycopy(buffer, 0, nbuf, 0, pos);
        //这里使用了原子变量引用更新,确保多线程环境下内存的可见性  
        if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
            // Can't replace buf if there was an async close.  
            // Note: This would need to be changed if fill()  
            // is ever made accessible to multiple threads.  
            // But for now, the only way CAS can fail is via close.  
            // assert buf == null;  
            throw new IOException("Stream closed");
        }
        buffer = nbuf;
    }
    count = pos;
    //从原始输入流中读取数据,填充缓冲区  
    int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
    //根据实际读取的字节数更新缓冲区中可用字节数  
    if (n > 0)
        count = n + pos;
}

整个fill的过程,可以看作是BufferedInputStream对外提供滑动读取的功能实现,通过预先读入一整段原始输入流数据至缓冲区中,而外界对BufferedInputStream的读取操作实际上是在缓冲区上进行,如果读取的数据超过了缓冲区的范围,那么BufferedInputStream负责重新从原始输入流中载入下一截数据填充缓冲区,然后外界继续通过缓冲区进行数据读取。

这样的设计的好处是:避免了大量的磁盘IO,因为原始的InputStream类实现的read是即时读取的,即每一次读取都会是一次磁盘IO操作(哪怕只读取了1个字节的数据),可想而知,如果数据量巨大,这样的磁盘消耗非常可怕。而通过缓冲区的实现,读取可以读取缓冲区中的内容,当读取超过缓冲区的内容后再进行一次磁盘IO,载入一段数据填充缓冲,那么下一次读取一般情况下就直接可以从缓冲区读取,减少了磁盘IO。

2.2 BufferedInputStream 比 InputStream快的原因

InputStream:每次从硬盘读入一个字到中转站, 再写入目的文件(硬盘)

BufferStream:一次读入n个字节到输入换成区,接着经中转站一个个写入到输出缓冲区,输入缓冲区为空时再次从硬盘读入批量数据,同理输出缓冲区满了以后再批量写入到目的文件(硬盘)。

如此使用BufferedStream可以减少访问硬盘的次数,速度大幅提升。

2.3 BufferedInputStream 和 BufferOutputStream的理解

BufferedInputStream的数据成员buf是一个位数组,默认为2048字节。当读取数据来源时,例如文件,BufferedInputStream会尽量将buf填满。当使用read()方法时,实际上是先读取buf中的数据,而不是直接对数据来源作读取。

当buf中的数据不足时,BufferedInputStream才会再实现给定的InputStream对象的read()方法,从指定的装置中提取数据。

BufferedOutputStream的数据成员buf也是一个位数组,默认为512字节。当使用write()方法写入数据时实际上会先将数据写到buf中,当buf已满时才会实现给定的OutputStream对象的write()方法,将buf数据写到目的地,而不是每次都对目的地作写入的动作。

3 应用

3.1 利用缓冲流进行拷贝,一个一个字节拷贝

        @Test
	public void IOTest1() throws Exception{
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D\\测试.avi"),2*1024);
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D\\测试2.avi"),2*1024);
		int len;
		while((len = bis.read()) != -1){
			bos.write(len);
		}
		bos.close();
		bis.close();
	}

3.2 利用缓冲流进行拷贝,多个字节多个字节拷贝

	@Test
	public void IOTest2() throws Exception{
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\测试.avi"));
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\测试copy1.avi"));
		byte[] b = new byte[2*1024];
		int len;
		while((len = bis.read(b)) != -1){
			bos.write(b, 0, len);
		}
		bos.close();
		bis.close();
	} 

3.3 利用缓冲流实现对文件的追加

	@Test
	public void IOTest3() throws Exception{
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\test.txt",true));
		bos.write("sadadas".getBytes());
		bos.close();
	}

3.4 利用缓冲流读取文件

	@Test
	public void IOTest4() throws Exception{
		FileInputStream fis = new FileInputStream("D:\\test.txt");
		int len;
		while((len = fis.read()) != -1){
			System.out.print((char)len);
		}
		fis.close();
	}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

有鹿如溪

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

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

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

打赏作者

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

抵扣说明:

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

余额充值