我们平时常常会对文件进行读取操作,如使用FileInputStream进行读取操作,则效率很低.为此我们可以使用缓冲字节流BufferedInputStream来操作,读取的效率会有很大的提升.在此我们介绍如何使用BufferedInputStream及分析其工作的原理.
一.使用介绍:
1.1定义:
BufferedInputStream是高级流,不能直接对文件进行操作,只有低级流才能直接与文件相连,所以需套接一个低级流,例如:
FileInputStream fis = new FileInputStream("test.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
1.2读取文件:
读取时,我们一般使用read()方法循环的方式读取,如读取到了文件末尾,则read()方法会返回-1,例如:
int len = -1;
while((len=bis.read())!=-1){
System.out.println(len);
}
通过循环读取的方式,可以将文件读取完毕.
二.原理分析:
通过以上内容了解了BufferedInputStream的使用方式,那其工作原理是如何的呢?下面将通过BufferedInputStream的源码分析来了解其工作的原理.
2.1首先分析下BufferedInputStream的属性和构造函数:
属性如下:
//默认的缓冲大小8k
private static int DEFAULT_BUFFER_SIZE = 8192;
/*最大的缓冲大小Integer.MAX_VALUE - 8,减8是由于虚拟机中在数组中保留了一些头信息*/
private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
//定义数据存储的缓冲字节数组
protected volatile byte buf[];
//原子属性更新器,用来保证对buf进行原子更新
private static final
AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater =
AtomicReferenceFieldUpdater.newUpdater
(BufferedInputStream.class, byte[].class, "buf");
//buf字节数组中实际数据的大小
protected int count;
//开始读取的位置
protected int pos;
//记录最后一次开始读取的位置
protected int markpos = -1;
//允许的最大提前读取量
protected int marklimit;
构造函数如下:
//以InputStream作为参数,缓冲区大小默认8k
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];
}
2.2方法分析:
2.2.1.read()方法分析:
//无参数的read()方法
public synchronized int read() throws IOException {
//如果开始读取的位置大于或等于缓冲区实际大小
if (pos >= count) {
//则填充缓冲区
fill();
//填充之后,读取位置还是大于或等于缓冲区实际大小,则读取完毕,返回-1
if (pos >= count)
return -1;
}
//返回缓冲区中的第一个字节
return getBufIfOpen()[pos++] & 0xff;
}
2.2.2.read1(byte[] b, int off, int len)方法分析:
此方法可以自定义的字节数组,以及开始读取的位置和实际读取的长度,源码如下:
private int read1(byte[] b, int off, int len) throws IOException {
//可读取的大小
int avail = count - pos;
//如果可读取大小小于或等于0
if (avail <= 0) {
//如果len大于或等于缓冲区大小且标记位置小于0,则按照给定的长度读取
if (len >= getBufIfOpen().length && markpos < 0) {
return getInIfOpen().read(b, off, len);
}
//否则按照默认大小读取
fill();
avail = count - pos;
//如果可读取大小小于或等于,则读取完毕,返回-1
if (avail <= 0) return -1;
}
//取avail和len之间的较小值
int cnt = (avail < len) ? avail : len;
//将缓冲区的字节数组从pos位置开始,长度为cnt的内容复制到b字节数组中off开始的位置
System.arraycopy(getBufIfOpen(), pos, b, off, cnt);
//读取位置增加cnt
pos += cnt;
//返回读取的长度
return cnt;
}
2.2.3.read(byte b[], int off, int len)方法分析:
public synchronized int read(byte b[], int off, int len)
throws IOException
{
//检查流是否关闭
getBufIfOpen(); // Check for closed stream
//通过"|"运算确保off,len大于或等于0,b.length大于或等于off+len
if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int n = 0;
//无限循环,只要剩余的内容满足长度len,则保证每次能读取到的长度是len
for (;;) {
//读取len长度字节
int nread = read1(b, off + n, len - n);
if (nread <= 0)
return (n == 0) ? nread : n;
//因为缓冲字节数组的长度是8192,假设len是800,则读取10次之后,缓冲字节剩余的长度是192,则第一次读取的长度就是192,n也就是192,此时,不会返回,会再次循环.再次循环时,则先将填充缓冲区,再读取剩余的608,读取到了800之后,则返回800的长度.
n += nread;
if (n >= len)
return n;
// if not closed but no bytes available, return
InputStream input = in;
//如果输入流中缓冲去可用的大小小于或等于0,则返回n
if (input != null && input.available() <= 0)
return n;
}
}
2.2.4.fill()方法分析:
fill()方法是用于读取数据并填充缓冲区.
private void fill() throws IOException {
//检查流是否关闭
byte[] buffer = getBufIfOpen();
//判断标记位置小于0
if (markpos < 0)
//设置位置为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;
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;
}
//设置大小为pos
count = pos;
//读取长度为默认大小的数据到缓冲区,并返回读取的长度
int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
if (n > 0)
//将count设置为长度大小
count = n + pos;
}
来源:oschina
链接:https://my.oschina.net/u/4390740/blog/4286443