BufferInputStream、BufferOutputStream缓冲流实现

一 介绍

JavaIO技术是很重要的技术, 现在来解释一下BufferInputStream、BufferOutputStream的具体实现。

那首先来看看BufferInputStream在程序中是怎么使用的,相信大家都知道怎么写。

/** 使用缓冲技术读取数据
	  * 
	  * */
	public static void bufferRead(String path) {
		BufferedInputStream bi=null;
		byte[] buf=new byte[1024];
		int length=0;
		try {
			 bi=new BufferedInputStream(new FileInputStream(path));
			 while((length=bi.read(buf))!=-1){
				 System.out.println(new String(buf,0,length));
			 }
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
				try {
					if(bi!=null){
					bi.close();
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
	}

二 缓冲技术

  不用怀疑,以上代码段使用的就是BufferInputStream缓冲流,那么问题来了, 首先为什么他可以提高流的读写效率?其次他是怎么实现的 ?  这是二个问题,不过读懂BufferInputStream源码应该都解决了。

2.1 装饰者设计模式

   这是23种设计模式其中之一, https://blog.csdn.net/huangchongwen/article/details/78687043 ,简单的说,比如FileInputStream的读写效率比较低,我们常常自定义缓冲数组来提高效率。现在我们交给一个对象来维护,并且进行功能增强。

装饰者设计模式框架如图2.1所示。

 BufferInputStream、BufferOutputStream缓冲流实现_第1张图片

                                                                    图2-1 装饰者设计模式框架图

2.2 BufferInputStream源码分析

   2.2.1 BufferInputStream、FilterInputStream的继承结构

  BufferInputStream、BufferOutputStream缓冲流实现_第2张图片


BufferInputStream、BufferOutputStream缓冲流实现_第3张图片


  2.2.2 BufferInputStream的源码

    几个重要属性
public
class BufferedInputStream extends FilterInputStream {
  private static int DEFAULT_BUFFER_SIZE = 8192; 
protected volatile byte buf[];
 protected int count;
 protected int pos;
。。。
}

 以上我们看到BufferedInputStream 继承了FilterInputStream ,有个大小为8k的buf[]缓冲数组。 protected int count;
protected int pos; 这个是访问指针,暂时不用管。 到这里就怀疑了,既然是装饰者设计模式,为什么没有被装饰者啊,存在的,在父类FilterInputStream

public
class FilterInputStream extends InputStream {
    /**
     * The input stream to be filtered.
     */
    protected volatile InputStream in;
    
}

 现在有必要从装饰者设计角度来解释一下,首先无论是装饰者(具有实际读写效果的类,如文件输入流)还是被装饰者(缓冲类)都是需要继承InputStream类的,这叫二者类型一致。然后FileInputStream 为被装饰者之一、FilterInputStream 为装饰者(缓冲类)抽象类,继承FilterInputStream的类如:BufferInputStream则是具体的装饰者之一。看到这里,这不就是没穿衣服的"装饰者设计模式"吗?赤裸裸啊。看的眼花,我们使用一张图解释IO流的整个设计体系(不仅仅是BufferInputStream)。

BufferInputStream、BufferOutputStream缓冲流实现_第4张图片

被装饰者

java.lang.Object
 
继承者java.io.InputStream

直接已知子类: AudioInputStream, ByteArrayInputStream, FileInputStream, FilterInputStream, InputStream, ObjectInputStream, PipedInputStream, SequenceInputStream, StringBufferInputStream

装饰者

java.lang.Object
 
继承者java.io.InputStream
     
继承者java.io.FilterInputStream

直接已知子类: BufferedInputStream, CheckedInputStream, CipherInputStream, DataInputStream, DeflaterInputStream, DigestInputStream, InflaterInputStream, LineNumberInputStream, ProgressMonitorInputStream, PushbackInputStream

不得不说, 图画的太low了。左边的为“被装饰者”,右边的为“装饰者”通过一个InputStream来维护一个具体的被装饰者。IO体系的对象绝不多数要不然就是左边的被装饰者,要不就是"装饰者" 比如: BufferInputStream缓冲类。有没发现之前学到字节流对象大部分在这里。 

好,到这里,我们在学习BufferInputStream的实现就好办多了。继续。

 while((length=bi.read(buf))!=-1){
     System.out.println(new String(buf,0,length));
    }

bi.read(buf))怎么执行的?FilterInputStream 抽象类 里面有个通用的read(byte b[])方式,就是自定义缓冲,装饰者大家通用,就写到抽象类里面最好了。那么BufferInputStream 有自己的read方式,那就override一下。

public class FilterInputStream{ 
public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
    }
 public int read(byte b[], int off, int len) throws IOException {
        return in.read(b, off, len);
    }

}

public class BufferInputStream extends FilterInputStream{
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);
            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;
        }
    }
        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)) {
                    // 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;
    }
}

  BufferInputStream 不容易明白,  其实代码是这样子的。在 read(byte b[], int off, int len)  的时候一次性读取8k的数据量缓冲到buf[], 然后操作对象变成BufferInputStream  属性buf[],你自己传进来byte[] 数组就是从buf[]数组中拿, 注意这个过程建立流通道读取数据就那么一次(直接读取8k)。假如数据大于8k,那么再从设备中取数据到buf[]中,缓冲起来,接着过程如上。
看到这里,你思考一下,读取效率是不是比较高了。


总结: 你可以通过装饰者设计模式来学习IO流体系,查看具体源码来查看方法如read(byte b[], int off, int len)的具体实现!





 

    


 

你可能感兴趣的:(Java工程师面试)