java IO流分析:ByteArrayInputstream 和 BufferedInputStream

InputStream的结构图如下(其中StringBufferInputStream 和 LineNumberInputStream 都已经过时):

每个实现可以针对不同的输入源,FileInputStream针对文件,ByteArrayInputStream针对内存byte数组等,

其中FilterInputStream是一个特殊的实现,应用了装饰者模式,其子类可以用来装饰其他实现类


先看看ByteArrayInputStream的源码: ByteArrayInputStream中有个byte数组,用来存储需要读取的数据, 读取时直接根据数组下标来获取对应的数据

还定义了2个int类型的变量pos和mark,其中,pos是用来标识下一个读取的数据在数组中的下标,每次读取后pos下标就增加相应的长度,指向下一次读取的下标

而mark则用来指向某个特定下标,初始化时一般指向第一个元素的下标,可以在读取到特定位置时使用mark=pos来定位到其他下标,也可以使用pos=mark来重新读取前面的数据

//内部数组用来保存初始化时传入的数组
protected byte buf[];
//下一个读取的数组下标
protected int pos;
//位置标记,默认为0,也可以使用mark()方法更改,用来标记到特定位置,使得读取到其他位置后可以用 reset()重置到mark标记的地方,重新开始读取
protected int mark = 0;
protected int count;
    //初始化时,直接将内部数组指向传入的数组,pos指向数组第一个元素
    public ByteArrayInputStream(byte buf[]) {
        this.buf = buf;
        this.pos = 0;
        this.count = buf.length;
    }
    //如果初始化时,希望从指定下标开始读取,只需要将pos更改为这个下标即可
    public ByteArrayInputStream(byte buf[], int offset, int length) {
        this.buf = buf;
        this.pos = offset;
        this.count = Math.min(offset + length, buf.length);
        this.mark = offset;
    }
    //read则直接根据下标返回数组元素(先判断是否越界)
    public synchronized int read() {
        return (pos < count) ? (buf[pos++] & 0xff) : -1;
    }
    //读取多个到指定数组

    public synchronized int read(byte b[], int off, int len) {
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        }

        if (pos >= count) {
            return -1;
        }

        int avail = count - pos;
        if (len > avail) {
            len = avail;
        }
        if (len <= 0) {
            return 0;
        }
        System.arraycopy(buf, pos, b, off, len);
        pos += len;
        return len;
    }

    //可以跳过指定长度的下标进行读取,实际只要将pos加上相应长度即可(在不越界的情况下)
    public synchronized long skip(long n) {
        long k = count - pos;
        if (n < k) {
            k = n < 0 ? 0 : n;
        }

        pos += k;
        return k;
    }

    public synchronized int available() {
        return count - pos;
    }

    public boolean markSupported() {
        return true;
    }

    //mark和pos的相互切换,使得读取过程中,可以返回到某些指定地方开始重新读取
    public void mark(int readAheadLimit) {
        mark = pos;
    }

    public synchronized void reset() {
        pos = mark;
    }


再看看BufferedInputStream, 源码看起来跟ByteArrayInputStream类似,这里使用装饰者模式, 构造函数中需要有一个inputstream,可以用来对fileinputstream等进行包装

跟ByteArrayInputStream不同的是这里有一个默认的缓存区,也可以自己定义大小,读取的时候先从缓存区读取数据,如果缓存区没有数据了,那么需要从输入源读取数据到缓存区

好处在于不用一次性把数据全部加载到内存,也不用频繁去请求输入源的数据, 具体体现在fill()方法上面,在read的时候会先判断缓冲区是否还有数据可读,没有就fill()

    public synchronized int read() throws IOException {
        if (pos >= count) {//无数据读取了,需要从缓冲区读取
            fill();
            if (pos >= count)
                return -1;
        }
        return getBufIfOpen()[pos++] & 0xff;
    }

    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;//长度为原来数组的2倍
                if (nsz > marklimit)
                    nsz = marklimit;
                byte nbuf[] = new byte[nsz];
                System.arraycopy(buffer, 0, nbuf, 0, pos);//先将原数组copy到新的数组
                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;
    }


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值