Java IO:BufferedInputStream使用详解及源码分析

来源:互联网 发布:最新前端技术编程语言 编辑:程序博客网 时间:2024/05/18 12:38

使用方法

  BufferedInputStream继承于FilterInputStream,提供缓冲输入流功能。缓冲输入流相对于普通输入流的优势是,它提供了一个缓冲数组,每次调用read方法的时候,它首先尝试从缓冲区里读取数据,若读取失败(缓冲区无可读数据),则选择从物理数据源(譬如文件)读取新数据(这里会尝试尽可能读取多的字节)放入到缓冲区中,最后再将缓冲区中的内容部分或全部返回给用户.由于从缓冲区里读取数据远比直接从物理数据源(譬如文件)读取速度快。

方法介绍

  BufferedInputStream提供的API如下:

//构造方法BufferedInputStream(InputStream in)BufferedInputStream(InputStream in, int size)//下一字节是否可读synchronized int     available()//关闭void     close()//标记, readlimit为mark后最多可读取的字节数synchronized void     mark(int readlimit)//是否支持mark, trueboolean     markSupported()//读取一个字节synchronized int     read()//读取多个字节到bsynchronized int     read(byte[] b, int off, int len)//重置会mark位置synchronized void     reset()//跳过n个字节synchronized long     skip(long n)

使用示例

public void testBufferedInput() {    try {        /**         * 建立输入流 BufferedInputStream, 缓冲区大小为8         * buffer.txt内容为         * abcdefghij         */        InputStream in = new BufferedInputStream(new FileInputStream(new File("buff.txt")), 8);        /*从字节流中读取5个字节*/        byte [] tmp = new byte[5];        in.read(tmp, 0, 5);        System.out.println("字节流的前5个字节为: " + new String(tmp));        /*标记测试*/        in.mark(6);        /*读取5个字节*/        in.read(tmp, 0, 5);        System.out.println("字节流中第6到10个字节为: " +  new String(tmp));        /*reset*/        in.reset();        System.out.printf("reset后读取的第一个字节为: %c" , in.read());    } catch (Exception e) {        e.printStackTrace();    }}

  运行结果如下:

字节流的前5个字节为: abcde字节流中第610个字节为: fghijreset后读取的第一个字节为: f

源码分析

构造方法

  BufferedInputStream的构造方法有两个,区别是缓冲区大小设置。

/** * Creates a <code>BufferedInputStream</code> * and saves its  argument, the input stream * <code>in</code>, for later use. An internal * buffer array is created and  stored in <code>buf</code>. * * @param   in   the underlying input stream. */public BufferedInputStream(InputStream in) {    this(in, DEFAULT_BUFFER_SIZE); //默认8192, 8M}/** * Creates a <code>BufferedInputStream</code> * with the specified buffer size, * and saves its  argument, the input stream * <code>in</code>, for later use.  An internal * buffer array of length  <code>size</code> * is created and stored in <code>buf</code>. * * @param   in     the underlying input stream. * @param   size   the buffer size. * @exception IllegalArgumentException if {@code size <= 0}. */public BufferedInputStream(InputStream in, int size) {    super(in);    if (size <= 0) {        throw new IllegalArgumentException("Buffer size <= 0");    }    buf = new byte[size];}

read方法

  read方法有每次读取一个字节和一次读取多个字节两种重载。下面主要分析读取多个字节的read方法。重点在于fill()方法

/** * Reads bytes from this byte-input stream into the specified byte array, * starting at the given offset. * * <p> This method implements the general contract of the corresponding * <code>{@link InputStream#read(byte[], int, int) read}</code> method of * the <code>{@link InputStream}</code> class.  As an additional * convenience, it attempts to read as many bytes as possible by repeatedly * invoking the <code>read</code> method of the underlying stream.  This * iterated <code>read</code> continues until one of the following * conditions becomes true: <ul> * * @param      b     destination buffer. * @param      off   offset at which to start storing bytes. * @param      len   maximum number of bytes to read. * @return     the number of bytes read, or <code>-1</code> if the end of the stream has been reached. * @exception  IOException  if this input stream has been closed by invoking its {@link #close()} method, *                   or an I/O error occurs. */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); //读取len长度的字节到b中        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;    }}/** * Check to make sure that buffer has not been nulled out due to * close; if not return it; */private byte[] getBufIfOpen() throws IOException {    byte[] buffer = buf;    if (buffer == null)        throw new IOException("Stream closed");    return buffer;}/** * Read characters into a portion of an array, reading from the underlying * stream at most once if necessary. */private int read1(byte[] b, int off, int len) throws IOException {    int avail = count - pos; //缓冲区中可读字节数    if (avail <= 0) { //没可读字节        /* If the requested length is at least as large as the buffer, and           if there is no mark/reset activity, do not bother to copy the           bytes into the local buffer.  In this way buffered streams will           cascade harmlessly. */        if (len >= getBufIfOpen().length && markpos < 0) { //没mark并且请求长度大于buff长度            return getInIfOpen().read(b, off, len); //直接从文件中读取,不走缓冲区        }        fill(); //修改或者扩展缓冲区        avail = count - pos; //可读字节数        if (avail <= 0) return -1;    }    int cnt = (avail < len) ? avail : len; //取最小值, 缓冲区中可能没有足够可读的字节    System.arraycopy(getBufIfOpen(), pos, b, off, cnt); //复制    pos += cnt;    return cnt;}/** * Fills the buffer with more data, taking into account * shuffling and other tricks for dealing with marks. * Assumes that it is being called by a synchronized method. * This method also assumes that all data has already been read in, * hence pos > count. */private void fill() throws IOException {    /**     * 填充字符时如果没有mark标记, 则直接清空缓冲区,然后将输入流的数据写入缓冲区     * 如果有mark标记,则分如下几种情况     * 1 普通mark,直接将标记以前的字符用标记以后的字符覆盖,剩余的空间读取输入流的内容填充     * 2 当前位置pos >= buffer的长度 >= marklimit,说明mark已经失效,直接清空缓冲区,然后读取输入流内容     * 3 buffer长度超出限制,抛出异常     * 4 marklimit比buffer的长度还大,此时mark还没失效,则扩大buffer空间     */    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); //将buffer的数据复制到nbuf中            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;}

mark\reset方法

/** * See the general contract of the <code>mark</code> * method of <code>InputStream</code>. * * @param   readlimit   the maximum limit of bytes that can be read before *                      the mark position becomes invalid. * @see     java.io.BufferedInputStream#reset() */public synchronized void mark(int readlimit) {    marklimit = readlimit;    markpos = pos;}/** * See the general contract of the <code>reset</code> * method of <code>InputStream</code>. * <p> * If <code>markpos</code> is <code>-1</code> * (no mark has been set or the mark has been * invalidated), an <code>IOException</code> * is thrown. Otherwise, <code>pos</code> is * set equal to <code>markpos</code>. * * @exception  IOException  if this stream has not been marked or, *                  if the mark has been invalidated, or the stream *                  has been closed by invoking its {@link #close()} *                  method, or an I/O error occurs. * @see        java.io.BufferedInputStream#mark(int) */public synchronized void reset() throws IOException {    getBufIfOpen(); // Cause exception if closed    if (markpos < 0)        throw new IOException("Resetting to invalid mark");    pos = markpos;}

参考:

[1] http://zhhphappy.iteye.com/blog/1562427
[2] http://blog.sina.com.cn/s/blog_67f995260101huxz.html
[3] http://www.cnblogs.com/skywang12345/p/io_12.html

0 0