mark(int readLimit)这个地方对readLimit有些迷惑,API中是这么解释的
The readlimit
arguments tells this input stream to allow that many bytes to be read before the mark position gets invalidated.
查看了一下源码,大致明白了一些。不想看源码的直接跳到最后。
BufferedInputStream源码:
先看几个属性:
protected volatile byte buf[]; 这是内部缓冲区,数据存储在这里面。
protected int pos; 这个属性用来标记buffer中的当前位置,即下一次read操作会从这个位置开始读取。
protected int markpos = -1; 这个用来记录我们mark操作时的pos值。
protected int marklimit; mark操作之后,read操作之前允许读取的最大值。
看一下mark函数:
public synchronized void mark(int readlimit) {
marklimit = readlimit;
markpos = pos;
}
很简单,就是存储mark时刻的状态。
看一下rese函数:
public synchronized void reset() throws IOException {
getBufIfOpen(); // Cause exception if closed
if (markpos < 0)
throw new IOException("Resetting to invalid mark");
pos = markpos;
}
也很简单,就是重新把指针移动到mark的地方,下次从此处开始读取。
下面看一下mark的参数readLimit
private void fill() throws IOException {
byte[] buffer = getBufIfOpen();
if (markpos < 0)
pos = 0; /* 没有mark的情况 */
else if (pos >= buffer.length) /* no room left in buffer */
if (markpos > 0) { //
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 { /* grow buffer */
int nsz = pos * 2;
if (nsz > marklimit)
nsz = marklimit;
byte nbuf[] = new byte[nsz];
System.arraycopy(buffer, 0, nbuf, 0, pos);
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;
}
上面的代码是用来填充缓冲区的,流程是这样的:如果没有mark()标记,可以直接填充;如果有mark标记,但是当前的pos位置小于缓冲区的长度,即缓冲区未填满,那么继续填充剩余的缓冲区;如果当前的pos已经到达缓冲区末尾,这时的操作取决于mark的位置,如果mark的位置大于0,那么缓冲区中mark之前的就没用了,直接复制markpos到缓冲区末尾的数据给当前的缓冲区;如果makpos等于0,这时分两种情况,如果缓冲区长度大于等于marklimit,这时相当于marklimit是无效的,因为可用数据比我们的marklimit还要大,肯定符合要求;如果缓冲区长度小于marklimit,这时因为markpos等于0,我们需要的缓冲区长度是marklimit-markpos=marklimit>bufferLength,所以缓冲区需要扩展了。
一句话解释一下:因为缓冲区大小是固定的,在某一位置mark之后,如果又进行了大量数据的读写,整个缓冲区可能因为被完全冲刷掉而导致无法进行后续的reset操作,这时调用reset就会抛出异常。相比之下ByteArrayInputStream就不会产生这个问题,因为它所有的数据都在内存中,不存在被冲刷的情况。