Java中IO框架——BufferedInputStream源码解析

本文详细介绍了Java中的BufferedInputStream类,它是FilterInputStream的子类,主要用于为其他输入流提供缓冲功能。文章深入分析了其构造函数、核心属性及关键方法如read、skip等的实现原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

BufferedInputStream 继承自 FilterInputStream 。
InputSteam 的源码分析见博客:http://blog.youkuaiyun.com/yx0628/article/details/79343633
FilterInputStream 的源码分析见博客:http://blog.youkuaiyun.com/yx0628/article/details/79366049
BufferedInputStream 的作用是为其它输入流提供缓冲功能。创建 BufferedInputStream 时,通过它的构造函数指定某个输入流为参数。BufferedInputStream 会将该输入流数据分批读取,每次读取一部分到缓冲中;操作完缓冲中的这部分数据之后,再从输入流中读取下一部分的数据。

属性

    // 默认的缓冲数组大小
    private static int DEFAULT_BUFFER_SIZE = 8192;
    // 最大的缓冲数组大小(这个最大值和ArrayList的数组最大值一致)
    private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
    // 缓冲字节数组
    protected volatile byte buf[];
    // 字节数组中最后一个有效字节的下标大1的位置
    protected int count;
    // 游标
    protected int pos;
    // 最后一次调用mark方法时游标pos的位置,初始为-1
    protected int markpos = -1;
    // 调用 mark 方法后,在后续调用 reset 方法失败之前所允许的最大提前读取量。
    protected int marklimit;
    private static final
        AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater =
        AtomicReferenceFieldUpdater.newUpdater
        (BufferedInputStream.class,  byte[].class, "buf");

构造函数

    // 根据传入的输入流对象和默认缓冲数组大小构造缓冲字节输入流
    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];
    }

方法

getInIfOpen()

获取输入流对象。

    private InputStream getInIfOpen() throws IOException {
        InputStream input = in;
        if (input == null)
            throw new IOException("Stream closed");
        return input;
    }

getBufIfOpen()

获取缓冲字节数组。

    private byte[] getBufIfOpen() throws IOException {
        byte[] buffer = buf;
        if (buffer == null)
            throw new IOException("Stream closed");
        return buffer;
    }

fill()

    private void fill() throws IOException {
        // 获取缓冲字节数组
        byte[] buffer = getBufIfOpen();
        if (markpos < 0)
            // 如果markpos为-1,说明没有调用过mark方法,pos置为0
            pos = 0;
        // 如果pos游标走到了数组的最后位置,说明数组已满,此时就不能再往缓冲数组中写入新数据了
        // 下面是几种情况来解决这个问题,使其回归正常
        else if (pos >= buffer.length)
            if (markpos > 0) {  //第一种情况
                // markpos大于0说明做过标记,那么标记到数组尾部的这些字节是不能弄丢的
                // 所以这里要将它们保存下来,保存的方法是将这些字节拷贝到数组从0开始的位置
                // sz就代表这些字节的数量
                int sz = pos - markpos;
                // 执行上边所说的数组拷贝,放在从0开始的位置
                System.arraycopy(buffer, markpos, buffer, 0, sz);
                // 自然,游标pos就要移到这些数据的末尾也就是sz处,即从这里可以继续存放新数据
                pos = sz;
                // 最后要将标记位置置为0
                markpos = 0;
            } else if (buffer.length >= marklimit) { //第二种情况
                // 如果缓冲数组特别大,设置的标记位置自然也没有什么意义了
                // 所以将标记位置置为-1,游标从0开始,丢弃所有缓冲数据
                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 { //第四种情况
                // 这里是判断还能不能扩张原数组二倍的大小,如果二倍后大小超过了最大值,那就使用最大值
                // 正常情况下就是数组的二倍大小
                int nsz = (pos <= MAX_BUFFER_SIZE - pos) ?
                        pos * 2 : MAX_BUFFER_SIZE;
                // 这个nsz不能超过最大的标记大小
                if (nsz > marklimit)
                    nsz = marklimit;
                // 然后建立一个新的字节数组,使用增加后的长度
                byte nbuf[] = new byte[nsz];
                // 将buffer数组中的所有数据全部拷贝到新数组nbuf中
                System.arraycopy(buffer, 0, nbuf, 0, pos);
                if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
                    throw new IOException("Stream closed");
                }
                // 将新的数组作为缓冲数组
                buffer = nbuf;
            }
        // 此时count即最后一个有效字节的下一位置,为pos的位置
        count = pos;
        // 从输入流中读取一定量的字节,从缓冲数组的pos处开始存,直到填满数组,读取的字节数量为n
        int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
        // 如果n大于0,那么count需要后移n个位置
        if (n > 0)
            count = n + pos;
    }

read()

    public synchronized int read() throws IOException {
        if (pos >= count) {
            // 已经读完缓冲区中的数据,则调用fill()从输入流读取下一部分数据来填充缓冲区
            fill();
            if (pos >= count)
                // 没有可读字节,返回-1
                return -1;
        }
        // 正常情况下,返回pos位置的字节,之后游标后移一位
        return getBufIfOpen()[pos++] & 0xff;
    }

read1(byte[] b, int off, int len)

    // 将缓冲区中的数据写入到字节数组b中,off是字节数组b的起始位置,len是写入长度
    private int read1(byte[] b, int off, int len) throws IOException {
        int avail = count - pos;
        // 这里缓冲数组中已经没有可读取字节的情况
        if (avail <= 0) {
            /* 如果读取的长度大于缓冲区的长度 并且没有markpos,则直接从原始输入流中进行读取,从而避免无谓的复制 */
            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中off开始的位置
        System.arraycopy(getBufIfOpen(), pos, b, off, cnt);
        // 游标右移cnt个位置
        pos += cnt;
        // 返回读取的字节数量cnt
        return cnt;
    }

read(byte b[], int off, int len)

    // 将缓冲区中的数据写入到字节数组b中,off是字节数组b的起始位置,len是写入长度
    public synchronized int read(byte b[], int off, int len)
        throws IOException
    {
        getBufIfOpen(); 
        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;
            InputStream input = in;
            if (input != null && input.available() <= 0)
                return n;
        }
    }

skip(long n)

忽略 n 个字节。

    public synchronized long skip(long n) throws IOException {
        getBufIfOpen();
        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()

    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/reset

    public synchronized void mark(int readlimit) {
        marklimit = readlimit;
        markpos = pos;
    }
    public synchronized void reset() throws IOException {
        getBufIfOpen(); // Cause exception if closed
        if (markpos < 0)
            throw new IOException("Resetting to invalid mark");
        pos = markpos;
    }
    public boolean markSupported() {
        return true;
    }

close()

    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()
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值