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. Devexpress VCL Build v2013 vol 13.2.5 发布

    支持xe6 了,但是承诺的功能在哪里? What's New in 13.2.5 (VCL Product Line)   New Major Features in 13.2 What's New ...

  2. 2018.09.29 bzoj3675: [Apio2014]序列分割(斜率优化dp)

    传送门 斜率优化dp经典题目. 首先需要证明只要选择的K个断点是相同的,那么得到的答案也是相同的. 根据分治的思想,我们只需要证明有两个断点时成立,就能推出K个断点时成立. 我们设两个断点分成的三段连 ...

  3. 2018.08.22 NOIP模拟 or(线段树)

    or [描述] 构造一个长度为 n 的非负整数序列 x,满足 m 个条件,第 i 个条件为x[li] | x[li+1] | - | x[ri]=pi. [输入] 第一行两个整数 n,m.接下来 m ...

  4. 2018.07.17 洛谷P1368 工艺(最小表示法)

    传送门 好的一道最小表示法的裸板,感觉跑起来贼快(写博客时评测速度洛谷第二),这里简单讲讲最小表示法的实现. 首先我们将数组复制一遍接到原数组队尾,然后维护左右指针分别表示两个即将进行比较的字符串的头 ...

  5. 解决sea_born和matplotlib画图中文显示的问题

    #以下解决mtpl中文显示问题 from pylab import * mpl.rcParams['font.sans-serif'] = ['SimHei'] #以下解决seaborn中文编码报错问 ...

  6. 可视化 linux 无法启动eclipse 报错No java virtual machine

    点击eclipse的时候会产生这个 解决方案: (1)找到eclipse的安装目录(我这个是远程连接) 注意: 点击这里可以进入命令行编辑模式 点开后 (2)给文件授权(默认是只读的) (3)对文件进 ...

  7. delete千万级别大表中的某部分数据

    如果表很大--千万级别的数据,又不能做truncate 操作,只能 delete 表中某部分数据时可以用以下来执行,这种方式只对大表操作时比较有效率,数据量小时不考虑 --示例如下 declare c ...

  8. hdu2041

    题目 这道题以前也看到过,但是没有写出来,我刚开始以为用循环遍历一边就可以了,结果我错了,没想到是用的斐波拉契推出来的,用的是递推的思想. 站在楼梯的第n级想一下,前一步是从哪里来的,问题就清楚了. ...

  9. hdu 2780 Su-Su-Sudoku(DFS数独)

    题目链接:hdu2780 #include<stdio.h> #include<string.h> #include<queue> #include<math ...

  10. 20、docker swarm

      Swarm是Docker官方提供的一款集群管理工具,其主要作用是把若干台Docker主机抽象为一个整体,并且通过一个入口统一管理这些Docker主机上的各种Docker资源.Swarm和Kuber ...