JDK1.8源码学习--io包(BufferedInputStream)

前言
 


月是一轮明镜,晶莹剔透,代表着一张白纸(啥也不懂)

央是一片海洋,海乃百川,代表着一块海绵(吸纳万物)

泽是一柄利剑,千锤百炼,代表着千百锤炼(输入输出)

月央泽,学习的一种过程,从白纸->吸收各种知识->不断输入输出变成自己的内容

希望大家一起坚持这个过程,也同样希望大家最终都能从零到零,把知识从薄变厚,再由厚变薄!
 

一.BufferedInputStream的作用:

         直接看源码注释(我的翻译可能不太准,如果道友们有更棒的理解,可以留言或者私信)

/**
 * A <code>BufferedInputStream</code> adds
 * functionality to another input stream-namely,
 * the ability to buffer the input and to
 * support the <code>mark</code> and <code>reset</code>
 * methods. When  the <code>BufferedInputStream</code>
 * is created, an internal buffer array is
 * created. As bytes  from the stream are read
 * or skipped, the internal buffer is refilled
 * as necessary  from the contained input stream,
 * many bytes at a time. The <code>mark</code>
 * operation  remembers a point in the input
 * stream and the <code>reset</code> operation
 * causes all the  bytes read since the most
 * recent <code>mark</code> operation to be
 * reread before new bytes are  taken from
 * the contained input stream.
 * BufferedInputStream向另一个输入流添加功能,即缓冲输入和支持mark和reset方法的能力。
 * 创建BufferedInputStream时,会创建一个内部缓冲区数组。当读取或跳过流中的字节时,
 * 内部缓冲区会根据需要从包含的输入流中重新填充,一次很多字节。mark操作记住输入流中的一个点,
 * reset操作导致在新字节被读取之前重新读取自最近的mark操作以来读取的所有字节取自包含的输入流。
 * @author  Arthur van Hoff
 * @since   JDK1.0
 */

二.类图:

 

三.成员变量: 


    //默认缓冲区大小
    private static int DEFAULT_BUFFER_SIZE = 8192;

    /**
     * 要分配的数组的最大大小。一些 VM 在数组中保留一些头字。
     * 尝试分配更大的数组可能会导致 OutOfMemoryError:请求的数组大小超出 VM 限制
     */
    private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;

    /**
     * 存储数据的内部缓冲区数组。必要时,它可能会被另一个不同大小的数组替换
     */
    protected volatile byte buf[];

    /**
     * 原子更新程序为 buf 提供 compareAndSet。这是必要的,因为关闭可以是异步的。
     * 我们使用 buf[] 的空值作为该流已关闭的主要指示符。 (“in”字段在关闭时也会被清零。)
     */
    private static final
        AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater =
        AtomicReferenceFieldUpdater.newUpdater
        (BufferedInputStream.class,  byte[].class, "buf");

    /**
     * 比缓冲区中最后一个有效字节的索引大 1 的索引。该值始终在0到buf.length范围内;
     * 元素buf[0]到buf[count-1]包含从底层输入流获得的缓冲输入数据。
     */
    protected int count;

    /**
     * 1.缓冲区中的当前位置。这是要从buf数组中读取的下一个字符的索引
     * 2.该值始终在0到count的范围内。如果小于count,则buf[pos]是下一个要作为输入提供的字节;
     * 如果它等于count,则下一个read或skip操作将需要从包含的输入流中读取更多字节
     */
    protected int pos;

    /**
     * 1.在最后一个mark方法被调用时pos字段的值
     * 2.该值始终在-1到pos的范围内。如果输入流中没有标记位置,则该字段为-1。
     * 如果输入流中有标记位置,则buf[markpos]是reset操作后要作为输入提供的第一个字节。
     * 如果markpos不是-1,则从位置buf[markpos]到buf[pos-1]的所有字节必须保留在缓冲区数组
     * (尽管它们可能会移动到缓冲区数组中的另一个位置,并对count、pos和markpos的值进行适当调整);
     * 除非pos和markpos之间的差异超过marklimit,否则它们可能不会被丢弃
     */
    protected int markpos = -1;

    /**
     * 在调用mark方法之后,在后续调用reset方法失败之前允许的最大预读。
     * 每当pos和markpos之间的差异超过marklimit时,可以通过将markpos设置为-1<来删除标记代码
     */
    protected int marklimit;

四.构造方法: 

                

/**
     * 创建一个BufferedInputStream并保存它的参数,输入流in,供以后使用。
     * 一个内部缓冲区数组被创建并存储在buf中。
     */
    public BufferedInputStream(InputStream in) {
        this(in, DEFAULT_BUFFER_SIZE);
    }

    /**
     * 使用指定的缓冲区大小创建一个BufferedInputStream,并保存它的参数,
     * 输入流in,供以后使用。长度为size的内部缓冲区数组被创建并存储在buf中。
     */
    public BufferedInputStream(InputStream in, int size) {
        super(in);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }

五.内部方法:  

                fill

 /**
     * 用更多数据填充缓冲区,考虑到处理标记的混洗和其他技巧。假设它正在被同步方法调用。
     * 此方法还假设所有数据都已读入,因此 pos > count。
     */
    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
                缓冲区太大,使标记 pos = 0 无效;删除缓冲区内容
                */
            } 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)) {
                    //如果有异步关闭,则无法替换 buf。注意:如果 fill() 可被多个线程访问,
                    // 则需要更改此设置。但就目前而言,CAS 失败的唯一方法是通过 close。断言 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;
    }

                read

    /**
     * 参见InputStream的read方法的通用约定。
     */
    public synchronized int read() throws IOException {
        if (pos >= count) {
            fill();
            if (pos >= count)
                return -1;
        }
        return getBufIfOpen()[pos++] & 0xff;
    }


    /**
     * 1.从此字节输入流中读取字节到指定的字节数组中,从给定的偏移量开始。

     * 2.该方法实现了InputStream类对应的InputStream.read(byte[], int, int) read方法的通用约定。
     * 作为额外的便利,它尝试通过重复调用底层流的read方法来读取尽可能多的字节。
     * 3.这个迭代的read一直持续到以下条件之一变为真:

     *  1)已读取指定数量的字节,

     *  2)底层流的read方法返回-1,表示文件结束,或者底层流的available方法返回零,
     *  表示进一步的输入请求将被阻止

     *  3)如果底层流上的第一个read返回-1以指示文件结束,则此方法返回-1。否则此方法返回实际读取的字节数。
     *  4.鼓励但不要求此类的子类尝试以相同的方式读取尽可能多的字节。
     */
    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;
        }
    }

              skip

    /**
     * 参见InputStream的skip方法的通用约定
     */
    public synchronized long skip(long n) throws IOException {
        getBufIfOpen(); // Check for closed stream
        if (n <= 0) {
            return 0;
        }
        long avail = count - pos;

        if (avail <= 0) {
            // If no mark position set then don't keep in buffer
            //如果没有设置标记位置,则不要保留在缓冲区中
            if (markpos <0)
                return getInIfOpen().skip(n);

            // Fill in buffer to save bytes for reset
            //填充缓冲区以保存用于重置的字节
            fill();
            avail = count - pos;
            if (avail <= 0)
                return 0;
        }

        long skipped = (avail < n) ? avail : n;
        pos += skipped;
        return skipped;
    }

                available

    /**
     * 1.返回可以从此输入流读取(或跳过)的字节数的估计值,而不会因下一次调用此输入流的方法而阻塞。
     * 下一次调用可能是同一个线程或另一个线程。单次读取或跳过这么多字节不会阻塞,但可能读取或跳过更少的字节
     * 2.此方法返回缓冲区中剩余要读取的字节数 (count - pos) 和调用java.io.FilterInputStream.in
     * .available() 的结果之和。
     */
    public synchronized int available() throws IOException {
        int n = count - pos;
        int avail = getInIfOpen().available();
        return n > (Integer.MAX_VALUE - avail)
                    ? Integer.MAX_VALUE
                    : n + avail;
    }

                mark

    /**
     * 参见InputStream<code>的mark方法的通用约定
     */
    public synchronized void mark(int readlimit) {
        marklimit = readlimit;
        markpos = pos;
    }

                reset

    /**
     * 1.参见InputStream的reset方法的通用约定
     * 2.如果markpos是-1(未设置标记或标记已失效),则抛出IOException。
     * 否则,pos设置为等于markpos
     */
    public synchronized void reset() throws IOException {
        getBufIfOpen(); // Cause exception if closed
        if (markpos < 0)
            throw new IOException("Resetting to invalid mark");
        pos = markpos;
    }

                markSupported

    /**
     * 测试此输入流是否支持mark和reset方法。BufferedInputStream的markSupported方法返回true。
     */
    public boolean markSupported() {
        return true;
    }

                close

    /**
     * 关闭此输入流并释放与该流关联的所有系统资源。一旦流关闭,
     * 进一步的 read()、available()、reset() 或 skip() 
     * 调用将抛出 IOException。关闭先前关闭的流没有任何效果
     */
    public void close() throws IOException {
        byte[] buffer;
        while ( (buffer = buf) != null) {
            if (bufUpdater.compareAndSet(this, buffer, null)) {
                InputStream input = in;
                in = null;
                if (input != null)
                    input.close();
                return;
            }
            // Else retry in case a new buf was CASed in fill()
            //否则重试以防在 fill() 中 CASed 新的 buf
        }
    }

六.总结:

                终于来到io包,不容易....

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值