1. 概念简介

  BufferedInputStream和BufferedOutputStream是带缓冲区的字节输入输出处理流。它们本身并不具有IO流的读取与写入功能,只是在别的流(节点流或其他处理流)上加上缓冲功能以提高效率,就像是把别的流包装起来一样,因此缓冲流是一种处理流。事实上,这两个处理流(BufferedInputStream和 BufferedOutputStream),加上BufferedReader和BufferedWriter,这四个流在设计时使用到的正是装饰设计模式通过装饰设计模式,得以在其他的流的基础上增加缓冲的功能

  当对文件或者其他数据源进行频繁的读写操作时,效率比较低,这时如果使用缓冲流就能够更高效的读写信息。因为缓冲流是先将数据缓存起来,然后当缓存区存满后或者手动刷新时再一次性的读取到程序或写入目的地。

2. 使用FileInputStream和FileOutputStream复制文件的原理图

3. 使用BufferedInputStream和BufferedOutputStream复制文件的原理图

3.1 MyBos

  由源码可知,BufferedOutputStream自身并没有实现将数据写入目的地的功能,真正的写入功能其实还是由它所装饰的字节输出流os来实现的。通过缓冲区的缓冲作用,增加了内存与内存之间的交互,减少了内存与磁盘的直接交互,以此提升I/O的效率。

  下面是在参考了BufferedOutputStream的源码之后,自己实现的缓冲字节输出流,原理如下:首先在MyBos内部包装了一个真正具有将数据输出到目的地功能的字节输出流os;还定义了一个字节缓冲数组buf,默认大小为8K,当调用write(byte b)或write(byte[] buf, int off, int len)方法将数据写出时,并不是直接将数据写出到目的地,首选是先将数据写到MyBos中自定义的缓冲区buf中,只有当缓冲区满了或者手动刷新的时候,才会将缓冲区的数据一次性写入到目的地。

自己实现的缓冲字节输出流的代码如下所示:

import java.io.IOException;
import java.io.OutputStream; public class MyBos { private byte[] buf; //缓冲字节数组
private int count; //记录目前缓冲数组中的有效字节数
private OutputStream os;//被装饰的底层输出字节流 //构造方法
public MyBos(OutputStream os) throws Exception {
this(os, 8192); //若不显式指定缓冲数组的大小,则默认分配8k空间
} public MyBos(OutputStream os, int size) throws Exception {
if(size < 0) {
throw new Exception("缓冲区空间大小分配错误:" + size);
}
this.os = os;
buf = new byte[size];
} /**
* 写出单个字节数据
* @param b 写出的字节数据
* @throws IOException
*/
public void write(int b) throws IOException {
if(count == buf.length) {//若count == buf.length,说明缓冲区buf已满,则先将缓冲区数据写出
os.write(buf); //调用os的wirte(byte[] buf)方法讲数据写出!
count = 0; //重置count
} buf[count++] = (byte)b; //将字节数据写入缓冲区,写入到count所在索引位置上
} /**
* 将字节数组b[offset, offset+len)部分写出
* @param b 字节数组
* @param offset 开始位置
* @param len 长度
* @throws Exception
*/
public void write(byte[] b, int offset, int len) throws Exception { if(offset < 0 || len < 0 || b.length < (offset + len)) {
throw new Exception("数组索引越界异常!");
} if(len > buf.length) { //若写出的字节数组b数据大于缓冲数组
flush(); //则先调用flush()方法将缓冲数组的数据写出
os.write(b, offset, len); //直接将数组b的数据通过os的wirte方法写出,不写入缓冲数组了
return;
} if(len > buf.length - count) { //若缓冲数组剩余空间不足len长度
flush(); //则先将缓冲数组的数据写出
} System.arraycopy(b, offset, buf, count, len);
count += len; } /**
* 刷新缓冲区
* @throws IOException
*/
public void flush() throws IOException {
if(count > 0) { //若缓冲区中有数据,则调用底层输出流os将数据写出
os.write(buf, 0, count);
os.flush();
count = 0; //重置count
}
} /**
* 关闭流
* @throws IOException
*/
public void close() throws IOException {
if(null != os) {
flush(); //先将缓冲数组的数据写出
os.close(); //接着关闭底层数据流os
}
}
}

3.2  MyBis

  由BufferedInputStream的源码可知,该类的内部同样有一个字节缓冲区buf,每次读取数据时,它会先检查缓冲区中是否有数据,若有则直接从缓冲区中取数,若是没有,则先通过底层输入流将指定大小(默认是8192个字节)的数据从底层读取到缓冲区中,再从缓冲区拿数。

  跟BufferedOutputStream一样,BufferedInputStream自身也没有实现将数据从源文件读入内存的功能,真正的读取功能其实还是由它所装饰的底层字节输入流is来实现的。通过缓冲区的缓冲作用,增加了内存与内存之间的交互,减少了内存与磁盘的直接交互,以此提升I/O的效率。

自己实现的BufferedInputStream示例代码:

import java.io.IOException;
import java.io.InputStream; public class MyBis {
//用于访问底层数据的底层流
private InputStream is;
//字节数组缓冲区
byte[] buf;
//默认缓存大小8k
public static final int DEFAULT_BUF_SIZE = 8192;
//记录缓冲区中 待读取的字节数
private int count;
//用于记录读取到当前 buf 中字节数据的位置
private int pos; //构造方法
public MyBis(InputStream is) throws Exception {
this(is, DEFAULT_BUF_SIZE);
} public MyBis(InputStream is, int size) throws Exception {
if(size < 0) {
throw new Exception("\"缓冲区空间大小分配错误:\" + size");
}
this.is = is;
buf = new byte[DEFAULT_BUF_SIZE];
} /**
* 从缓冲区中读取下一个字节的数据,如果数据到到了末尾,返回 -1
* @return 读取到的字节数据的 int 形式,如果读取到了流的末尾,返回 -1
* @throws IOException
*/
public int read() throws IOException {
if(pos == count) {//若读取的下一个字节数已经到达有效数据的末尾
count = is.read(buf);//则通过底层的输入流,一次性读取 8192 个字节数据到缓冲区中来。
if(-1 == count) {//若is 没有读取到数据,直接返回-1
return -1;
}
//若is读取到数据了,将pos设置为第一个字节
pos = 0;
} //返回读取到的字节数据的 int 形式
return buf[pos++] & 0xff;
} /**
* 处理流的关闭问题:只关闭处理流即可,处理流的关闭会将底层的流关闭掉。
* @throws IOException
*/
public void close() throws IOException {
if(null != is) {
is.close();
}
}
}

测试自己写的字节缓冲输入流:

import java.io.FileInputStream;
import java.io.IOException; public class TestMyBis {
public static void main(String[] args) {
MyBis mb = null; try {
mb = new MyBis(new FileInputStream("./src/res/1.txt")); int value = 0; while(-1 != (value = mb.read())) {
System.out.print((char)value);
} } catch (Exception e) {
e.printStackTrace();
} finally {
if(null != mb) {
try {
mb.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

代码运行效果:

4. 字节缓冲流应用示例

示例代码:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException; public class BufferedStreamTest {
public static void main(String[] args) {
BufferedInputStream bis = null;
BufferedOutputStream bos = null; try {
//使用缓冲流
bis = new BufferedInputStream(new FileInputStream("e:/test_file/data_structure.avi"));
bos = new BufferedOutputStream(new FileOutputStream("e:/test_file/data_structure_copy.avi"));
int len = 0;
byte[] buf = new byte[1024*1024];//因为读取的源文件较大,所以这里分配的空间大一点 while(-1 != (len = bis.read(buf))) {
bos.write(buf, 0, len);
} } catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(null != bos) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(null != bis) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} }
}

最后,由BufferedInputStream和BufferedOutputStream这两个类的源码或API可以看出,它们没有新增任何新的方法,使用的都是常见的read()、read(byte[] b)、write(int b)、write(byte[] buf, int off, int len)等方法。

J05-Java IO流总结五 《 BufferedInputStream和BufferedOutputStream 》的更多相关文章

  1. Java IO(十) BufferedInputStream 和 BufferedOutputStream

    Java IO(十)BufferedInputStream 和 BufferedOutputStream 一.BufferedInputStream 和 BufferedOutputStream (一 ...

  2. java IO流 (五) 转换流的使用 以及编码集

    转换流的使用 1.转换流涉及到的类:属于字符流InputStreamReader:将一个字节的输入流转换为字符的输入流解码:字节.字节数组 --->字符数组.字符串 OutputStreamWr ...

  3. Java IO流学习总结三:缓冲流-BufferedInputStream、BufferedOutputStream

    Java IO流学习总结三:缓冲流-BufferedInputStream.BufferedOutputStream 转载请标明出处:http://blog.csdn.net/zhaoyanjun6/ ...

  4. Java IO流详解(五)——缓冲流

    缓冲流也叫高效流,是处理流的一种,即是作用在流上的流.其目的就是加快读取和写入数据的速度. 缓冲流本身并没有IO功能,只是在别的流上加上缓冲效果从而提高了效率.当对文件或其他目标频繁读写或操作效率低, ...

  5. Java IO流 BufferedInputStream、BufferedOutputStream的基本使用

    BufferedInputStream.BufferedOutputStream的基本使用 BufferedInputStream是FilterInputStream流的子类,FilterInputS ...

  6. Java IO流题库

    一.    填空题 Java IO流可以分为   节点流   和处理流两大类,其中前者处于IO操作的第一线,所有操作必须通过他们进行. 输入流的唯一目的是提供通往数据的通道,程序可以通过这个通道读取数 ...

  7. Java IO流01-总叙

     Java IO包体系结构图: 1.流式部分――IO的主体部分: 2.非流式部分——主要包含一些辅助流式部分的类,如:File类.RandomAccessFile类和FileDescriptor等类: ...

  8. Java IO流学习

    Java IO流学习 Java流操作有关的类或接口: Java流类图结构: 流的概念和作用 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是 ...

  9. Java IO流详解(一)——简单介绍

    文件在程序中是以流的形式来传输的.所以用Java来传输文件就得使用到Java IO流. 1.流的概念和作用 流:代表任何有能力产出数据的数据源对象或者是有能力接受数据的接收端对象<Thinkin ...

  10. Java:IO流与文件基础

    Java:IO流与文件基础 说明: 本章内容将会持续更新,大家可以关注一下并给我提供建议,谢谢啦. 走进流 什么是流 流:从源到目的地的字节的有序序列. 在Java中,可以从其中读取一个字节序列的对象 ...

随机推荐

  1. hdu-1728(bfs+优化)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1728 注意:1.先输入起点(y1,x1)和终点(y2,x2): 2.如果一个一个遍历会超时. 思路:每 ...

  2. ArcGIS Desktop python Add-in 测试一个插件

    a)制作一个插件文件 先找到工作目录,双击运行makeaddin.py脚本.这个脚本拷贝所有插件需要的文件和文件夹并在工作目录形成一个压缩文件.该压缩文件名为工作目录名称加上".esriad ...

  3. id 与 void * 转换

    MRC 环境下: id 变量赋值给 void * 变量运行时不会有问题. id obj1 = [NSObject new];void * p = obj1; void * 变量赋值给 id 变量并调用 ...

  4. CreateDialog()与CreateDialogIndrect()

    CreateDialog() 概述 函数功能:CreateDialog宏从一个对话框模板资源创建一个无模式的对话框,CreateDiaog宏使用CreateDialogParam函数. 函数原型:HW ...

  5. EF学习笔记-CODE FIRST-约定

    首先EF对关系数据库的映射遵循如下规则: Fluent API 配置 override 数据注释 override 约定 System.Data.Entity.ModelConfiguration.C ...

  6. REST格式

    首先要明确一点:REST 实际上只是一种设计风格,它并不是标准.(所以你可以看到网上一大堆的各种最佳实践,设计指南,但是没有人说设计标准).aisuhua/restful-api-design-ref ...

  7. NOR Flash的学习

    NOR Flash简介    NOR FLASH是INTEL在1988年推出的一款商业性闪存芯片,它需要很长的时间进行抹写,大半生它能够提供完整的寻址与数据总线,并允许随机存取存储器上的任何区域,而且 ...

  8. Firemonkey里触发home按键被按下的事件

    吾八哥我最近在使用Delphi里的Firemonkey平台写一个叫“由由密码管家”的APP工具,是跨多平台的,如ios/android/windows/macOs.由于是用于密码管理的,那么在手机里操 ...

  9. SQL Server 索引基本概念与优化

    数据页和区 页 SQL Server 中的数据以“页”(Page)的形式保存数据,页是SQL Server 的IO单位,读/写一次至少是一页.一页为8K(8192byte). 页由三部分组成,页头,数 ...

  10. signalR常见问题

    一.安装signalR会对应安装自己的NewJson包,如果引用了含有不同NewJson包的dll组件,会造成版本不一致.必须在运行环境中指出使用目标版本. 问题截图: 解决方式: <runti ...