赞
踩
BufferedInputStream和BufferedOutputStream都是缓冲流,缓冲流实际上里面有一个缓冲区,即字节数组,用流读取存储在硬盘上较大文件的时,频繁从硬盘中读取数据效率比较低,花费时间较长.缓冲流的作用就是将数据先读取到内存里面,然后从内存里面读取数据,读取速度得到很大的提升.
1.构造方法
- public BufferedInputStream(InputStream in) {this(in, DEFAULT_BUFFER_SIZE);}
- public BufferedInputStream(InputStream in, int size) {}
2.内部变量
- protected volatile byte buf[];
- protected int count;
- protected int pos;
- protected int markpos = -1;
- protected int marklimit;
3.内部方法
- public synchronized int read() throws IOException {}
- public synchronized int read(byte b[], int off, int len) throws IOException {}
- public synchronized long skip(long n) throws IOException {}
- public synchronized int available() throws IOException {}
- public synchronized void mark(int readlimit) {}
- public synchronized void reset() throws IOException {}
- public boolean markSupported() {}
- public void close() throws IOException {}
BufferedOutputStream介绍
1.构造方法
- public BufferedOutputStream(OutputStream out) { this(out, 8192);}
- public BufferedOutputStream(OutputStream out, int size) {}
2.内部变量
- protected byte buf[];
- protected int count;
3.内部方法
- public synchronized void write(int b) throws IOException {}
- public synchronized void write(byte b[], int off, int len) throws IOException {}
- public synchronized void flush() throws IOException {}
通过BufferedInputStream和BufferedOutputStream复制,效率极大提高.
- public class BufferedInputStreamDemo {
- public static void main(String[] args) throws IOException {
- File source = new File("D:\\软件.zip");
- File dest = new File("D:\\copy\\soft.zip");
- copyFile(source, dest);
- }
-
- private static void copyFile(File source, File dest) throws IOException {
- BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source));
- BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dest));
- byte buffer[] = new byte[1024];
- int len = 0;
- while ((len = bis.read(buffer)) != -1) {
- bos.write(buffer);
- }
- bis.close();
- bos.close();
- }
- }
BufferedInputStream源码分析
- public class BufferedInputStream extends FilterInputStream {
- // 默认缓冲区的大小
- private static int DEFAULT_BUFFER_SIZE = 8192;
- // 缓冲字节数组可扩展到最大字节
- private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
- // 缓冲区为一个字节数组
- protected volatile byte buf[];
- // 返回的是一个原子更新器,保证字节数组buf操作是原子性的.
- private static final AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater =
- AtomicReferenceFieldUpdater.newUpdater(BufferedInputStream.class, byte[].class, "buf");
- // 缓冲区里面有效的字节数
- protected int count;
- // 缓冲区里面当前的位置索引
- protected int pos;
- // 缓冲区里面标记的位置,也就是最后一次调用mark()方法时,记录pos的值.调用reset()时会将pos值重置为markpos的值
- protected int markpos = -1;
- // marklimit是mark()标记后,在调用reset()之前可读取的最大的字节数,限制标记后buffer的最大值
- protected int marklimit;
-
- // 获取输入流,将输入流数据读取到缓冲区里面
- private InputStream getInIfOpen() throws IOException {
- InputStream input = in;
- if (input == null)
- throw new IOException("Stream closed");
- return input;
- }
-
- // 获取缓冲字节数组
- private byte[] getBufIfOpen() throws IOException {
- byte[] buffer = buf;
- if (buffer == null)
- throw new IOException("Stream closed");
- return buffer;
- }
-
- // 创建缓冲输入流,里面缓冲区是一字节数组,大小为默认的8192字节
- public BufferedInputStream(InputStream in) {
- this(in, DEFAULT_BUFFER_SIZE);
- }
-
- // 创建缓冲输入流,并且指定缓冲区的大小
- public BufferedInputStream(InputStream in, int size) {
- super(in);
- if (size <= 0) {
- throw new IllegalArgumentException("Buffer size <= 0");
- }
- buf = new byte[size];
- }
-
- // 调用fill()方法将输入流中数据填充到缓冲区里面.
- // 1.没标记的情况下,读完缓冲区数据,不再保存已被读取过的数据.
- // 2.有标记的情况下,
- // 1)标记的位置markpos>0,需要保存标记之后的数据,标签之前的数据可丢弃
- // 2)标记的位置为0,而marklimit小于buffer.length,标记失效
- // 3)标记的位置为0,而marklimit大于buffer.lenght,可以扩容buffer.
- private void fill() throws IOException {
- byte[] buffer = getBufIfOpen();
- if (markpos < 0)
- pos = 0; /* no mark: throw away the buffer */
- else if (pos >= buffer.length) /* no room left in buffer */
- if (markpos > 0) { /* can throw away early part of the buffer */
- int sz = pos - markpos;
- System.arraycopy(buffer, markpos, buffer, 0, sz);
- pos = sz;
- markpos = 0;
- } else if (buffer.length >= marklimit) {
- markpos = -1; /* buffer got too big, invalidate mark */
- pos = 0; /* drop buffer contents */
- } else if (buffer.length >= MAX_BUFFER_SIZE) {
- throw new OutOfMemoryError("Required array size too large");
- } else { /* grow buffer */
- int nsz = (pos <= MAX_BUFFER_SIZE - pos) ? pos * 2 : MAX_BUFFER_SIZE;
- if (nsz > marklimit)
- nsz = marklimit;
- byte nbuf[] = new byte[nsz];
- System.arraycopy(buffer, 0, nbuf, 0, pos);
- if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
- throw new IOException("Stream closed");
- }
- buffer = nbuf;
- }
- count = pos;
- int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
- if (n > 0)
- count = n + pos;
- }
-
- // 此方法表示的是从缓冲区里面获取一个字节
- public synchronized int read() throws IOException {
- // 当缓冲区数据全部被读取完,会先填充缓冲区
- if (pos >= count) {
- fill();
- if (pos >= count)
- return -1;
- }
- return getBufIfOpen()[pos++] & 0xff;
- }
-
- // 将缓冲区的数据写到字节数组b里面,off是字节数组b的起始位置,长度为len
- private int read1(byte[] b, int off, int len) throws IOException {
- int avail = count - pos;
- // 缓冲区没有可读数据,如果要读取的字节数长度大于缓冲区长度,且没有标记的情况下
- // 直接从输入流中读取字节
- if (avail <= 0) {
- if (len >= getBufIfOpen().length && markpos < 0) {
- return getInIfOpen().read(b, off, len);
- }
- // 否则调用fill()填充缓冲区
- fill();
- avail = count - pos;
- if (avail <= 0)
- return -1;
- }
- // 实际能够读取的长度是avail
- int cnt = (avail < len) ? avail : len;
- // 缓冲区当前位置pos,复制cnt长度到字节数组b中
- System.arraycopy(getBufIfOpen(), pos, b, off, cnt);
- pos += cnt;
- return cnt;
- }
-
- // 将缓冲区的数据写到字节数组b里面,off是字节数组的起始位置,长度为len
- public synchronized int read(byte b[], int off, int len) throws IOException {
- getBufIfOpen(); // Check for closed stream
- if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
- throw new IndexOutOfBoundsException();
- } else if (len == 0) {
- return 0;
- }
-
- int n = 0;
- for (;;) {
- int nread = read1(b, off + n, len - n);
- //nread<=0,没有可读的字节数
- if (nread <= 0)
- return (n == 0) ? nread : n;
- n += nread;
- if (n >= len)
- return n;
- // if not closed but no bytes available, return
- InputStream input = in;
- if (input != null && input.available() <= 0)
- return n;
- }
- }
关于缓冲输入流填充方法fill().存在多种情况,分析如下:
1.缓冲区没有标记的情况下,即markpos<0时,缓冲区中已被读取过的字节不需要保存.即pos>=count时,会将pos和count重置为0.然后从输入流读取数据填充缓冲区0到buffer.length之间的位置.
2.缓冲区标记的情况下.即markpos>=0情况下,
1)当markpos>0的时,下次刷新缓冲的时候,需要将markpos到pos之间数据保留.从输入流中读取数据填充pos到buffer.length之间的位置.如下图,当markpos>0时,并且pos>=buffer.length.下次读取输入流的里面数据的时候,会将markpos到pos之间的数据保存.
2)当markpos=0的时候,buffer.length>marklimit,并且pos>buffer.length表示的是缓冲区里面没有剩余的位置.如果继续保留markpos到pos之间数据,那么刷新缓冲区之后,缓冲区依旧没有剩余位置,因为pos>=buffer.length.缓冲区没有空出位置,所以marklimit存在意义就是进行一个限定,根据buffer.length与marklimit大小来判断标记是否失效.源码中buffer.length>=marklimit的时候,标记失效.缓冲区里面0到buffer.length之间的位置是空的.可以进行填充.
3)当markpos=0.buffer.length<marklimit,并且pos>buffer.length表示缓冲区没有剩余位置.则需要扩容.
如下,扩充后容量的是2*pos和marklimit中的最小值.扩容后缓冲区会保留原先0-pos之间的数据.然后从输入流继续读取数据.
- public class BufferedOutputStream extends FilterOutputStream {
- // 缓冲输出流中的缓冲区为字节数组
- protected byte buf[];
- // 缓冲区有效的数据大小
- protected int count;
-
- // 创建默认大小为8192字节的缓冲输出流
- public BufferedOutputStream(OutputStream out) {
- this(out, 8192);
- }
-
- // 创建指定大小size的缓冲输出流
- public BufferedOutputStream(OutputStream out, int size) {
- super(out);
- if (size <= 0) {
- throw new IllegalArgumentException("Buffer size <= 0");
- }
- buf = new byte[size];
- }
-
- // 将缓冲区里面有效字节数写到输出流out中
- private void flushBuffer() throws IOException {
- if (count > 0) {
- out.write(buf, 0, count);
- count = 0;
- }
- }
-
- // 将数据b写到缓冲区里面
- public synchronized void write(int b) throws IOException {
- // 缓冲区已满情况下,会将数据写到输出流中
- if (count >= buf.length) {
- flushBuffer();
- }
- // 将字节b写到缓冲区里面
- buf[count++] = (byte) b;
- }
-
- // 将数据写入到缓冲区里面
- public synchronized void write(byte b[], int off, int len) throws IOException {
- // 如果写入的长度大于缓冲区长度,先刷新缓冲区,将缓冲区所有的数据写到输出流out去
- // 然后将要写入缓冲区的字节数组b,直接写到输出流
- if (len >= buf.length) {
- /*
- * If the request length exceeds the size of the output buffer, flush the output buffer and then write the data
- * directly. In this way buffered streams will cascade harmlessly.
- */
- flushBuffer();
- out.write(b, off, len);
- return;
- }
- // 如果写入缓冲区的数据长度大于缓冲区剩余的长度,将缓冲区里面数据写到输出流
- // 然后将字节数组b,起始位置off,长度为len数据写到缓冲区里面
- if (len > buf.length - count) {
- flushBuffer();
- }
- System.arraycopy(b, off, buf, count, len);
- count += len;
- }
-
- // 刷新缓冲区,将缓冲区里面数据写到输出流中
- public synchronized void flush() throws IOException {
- flushBuffer();
- out.flush();
- }
- }
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。