前言
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就行了。