FileInputstream
FileInputstream提供读取文件数据的方法,他们底层调用的是native方法
- native read0() 每次读出1个字节
- native int readBytes(byte b[], int off, int len) 读出数据到b字节数组中,并可以指定off【开始写入的位置】,【len】可最大读入len个字节到b数组, 返回【int】表示本次读取了几个字节
private native int read0() throws IOException;
private native int readBytes(byte b[], int off, int len) throws IOException;
public int read() throws IOException {
return read0();
}
//将数据读入数组,返回读取的个数
public int read(byte b[]) throws IOException {
return readBytes(b, 0, b.length);
}
//off:读入的数据先放在off开始的地方,最多放入len个byte【读入的数据可能比len少】
public int read(byte b[], int off, int len) throws IOException {
return readBytes(b, off, len);
}
BufferedInputStream
public class BufferedInputStream extends FilterInputStream
【BufferedInputStream 继承 FilterInputStream】
先来看BufferedInputStream 和 FilterInputStream 构造方法
public BufferedInputStream(InputStream in) {
this(in, DEFAULT_BUFFER_SIZE);
}
public BufferedInputStream(InputStream in, int size) {
//去FilterInputStream
super(in);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}
public class FilterInputStream extends InputStream {
protected volatile InputStream in;
protected FilterInputStream(InputStream in) {
this.in = in;
}
}
发现:构造BufferedInputStream对象的过程中需要一个InputStream类型,并会生成一个byte[] buf
数组的大小我们可自己指定,也可以不指定,使用默认大小DEFAULT_BUFFER_SIZE= 8192
buf数组的奥妙
重点代码
protected int markpos = -1;
protected int pos;//当前应该读取字节的位置
protected int marklimit;
protected int count;//读取的最终位置
public synchronized int read() throws IOException {
if (pos >= count) {
fill(); // 填充数组
if (pos >= count)
return -1;
}
return getBufIfOpen()[pos++] & 0xff;// pos指向的地方还没读取
}
private void fill() throws IOException {
byte[] buffer = getBufIfOpen();
if (markpos < 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;
}
count = pos;
int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
if (n > 0)
count = n + pos;
}
先介绍方法getBufIfOpen()和getInIfOpen()
getBufIfOpen() :如果buf数组不为null, 就返回buf数组。否则抛异常new IOException(“Stream closed”)告诉你流关闭了
getInIfOpen() :如果inputstream不为null, 就返回inputstream。否则抛异常new IOException(“Stream closed”)告诉你流关闭了
假设buf数组长度是5,在第一次调用read函数的时候,他先通过InputStream读取多个字节保存在buf数组中。
然后返回getBufIfOpen()[pos++] & 0xff,他会将pos处的字节返回给你,第一次会返回pos=0处的字节。然后将pos++, 指向数组的下一个位置。
如果你第二次调用read(),程序先判断【pos >= count】
因为pos<count, 说明数组中还有未读的字节,不需要填充数组!直接返回
getBufIfOpen()[pos++] & 0xff,即将pos=1处的字节返回。
如果pos>=count,说明buf数组的内容全部读取完毕,接下来再次调用fill方法从inputstream中读取一些数据到buf中。
比较
FileInputstream支持单字节读取,他是真实的单字节读取,读取一个字节就进行了一次磁盘IO
BufferedInputStream会通过FileInputstream进行一次磁盘IO, 一口气读取多个数据先到自己的buf数组中【这样数据就在内存中】,后面即使你只读取1个字节,直接去buf中慢慢取。这样会减少直接对文件的IO,因为读取内存的数据更快。