IO体系——ByteArrayInputStream源码解析

前言

IO体系可以分为节点流和包装流,节点流有发生在硬盘上的,如FileInputStream,其原始数据来源于硬盘上,而ByteArrayInutStream也属于一种节点流,其原始数据来源于内存中。
这是一个内存中的流。

ByteArrayInputStream

从字面意思可以看出,其内部肯定有byte[]数组,它是一个底层由byte[]数组构成的流对象。其内部构造和BufferInputStream很像。不同的是BufferInputStream内部固定了8字节缓存数组。而ByteArrayInputStream没有规定大小。具体来看下成员变量:

protected byte buf[];  数据容器
protected int pos;  当前读取位置
protected int mark = 0; 标记位置
protected int count;  数据长度

很简单的结构,那么它的流来源于哪里?
与节点流FileInputStream对比,FileInputStream的数据通过借助操作系统从磁盘中读取数据装在byte[]数组中。而ByteArrayInputStream是由构造者将它的字节数据装入ByteArrayInputStream的成员数组中。

特点

1. 缓存数据流

ByteArrayInputStream的构造方法中只需要传入一个byte[]数据,就能构建流。biruString、基本数据类型、其他可以转成byte[]的对象,都可以通过构造方法转化为ByteArrayInputStream。

public ByteArrayInputStream(byte buf[]) {
    this.buf = buf;
    this.pos = 0;
    this.count = buf.length;
}

由于该流对象是在内存中,因此一旦进程结束,流也会消失,不能持久化。

2. 用完不需要close

close方法来自父类InputStream, 由于这是一个内存流,内部没有持有FileDescriptor成员变量,不存在对系统文件句柄的占用,因此不需要释放文件句柄,所以ByteArrayInputStream用完不需要close。因此在close方法中没有任务操作;

 public void close() throws IOException {
}
3. 支持反复读取

这是一个在内存中的缓存流,它支持一些重复读取流的方法。具体是怎样操作的呢?
看一下两个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;
}

由于是对同一对象的成员变量操作,存在多线程并发安全问题,所以加了synchronized锁。
在单字节read()中,如果当前没有读到读完,则返回buf[]数组中当前下标的值,否则返回-1;
在批量字节读取中,

System.arraycopy(buf, pos, b, off, len);

将buf[]数组中的数据copy了一份出来,然后并没有清空ByteArrayInputStream中的数据。所以读取过程相当于是复制操作,读完以后依然可以读取下一次。
但是要读取下一次必须要将当前位置重置到buf[]数组首部,可以重新读一个完整的或者部分流,这取决与当前标记mark标记的位置。

public void mark(int readAheadLimit) {
    mark = pos;
}

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

除此以外,还有skip方法跳跃读取流,可以在当前下标位置跳过n字节:

public synchronized long skip(long n) {
    long k = count - pos;
    if (n < k) {
        k = n < 0 ? 0 : n;
    }

    pos += k;
    return k;
}

最后

整个ByteArrayInputStream比较简单,记住它是一个缓存在内存中的流,不需要close就行了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值