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()
}
}