JavaIO技术是很重要的技术, 现在来解释一下BufferInputStream、BufferOutputStream的具体实现。
那首先来看看BufferInputStream在程序中是怎么使用的,相信大家都知道怎么写。
/** 使用缓冲技术读取数据
*
* */
public static void bufferRead(String path) {
BufferedInputStream bi=null;
byte[] buf=new byte[1024];
int length=0;
try {
bi=new BufferedInputStream(new FileInputStream(path));
while((length=bi.read(buf))!=-1){
System.out.println(new String(buf,0,length));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(bi!=null){
bi.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
不用怀疑,以上代码段使用的就是BufferInputStream缓冲流,那么问题来了, 首先为什么他可以提高流的读写效率?其次他是怎么实现的 ? 这是二个问题,不过读懂BufferInputStream源码应该都解决了。
这是23种设计模式其中之一, https://blog.csdn.net/huangchongwen/article/details/78687043 ,简单的说,比如FileInputStream的读写效率比较低,我们常常自定义缓冲数组来提高效率。现在我们交给一个对象来维护,并且进行功能增强。
装饰者设计模式框架如图2.1所示。
图2-1 装饰者设计模式框架图
public
class BufferedInputStream extends FilterInputStream {
private static int DEFAULT_BUFFER_SIZE = 8192;
protected volatile byte buf[];
protected int count;
protected int pos;
。。。
}
以上我们看到BufferedInputStream 继承了FilterInputStream ,有个大小为8k的buf[]缓冲数组。 protected int count;
protected int pos; 这个是访问指针,暂时不用管。 到这里就怀疑了,既然是装饰者设计模式,为什么没有被装饰者啊,存在的,在父类FilterInputStream
public
class FilterInputStream extends InputStream {
/**
* The input stream to be filtered.
*/
protected volatile InputStream in;
}
现在有必要从装饰者设计角度来解释一下,首先无论是装饰者(具有实际读写效果的类,如文件输入流)还是被装饰者(缓冲类)都是需要继承InputStream类的,这叫二者类型一致。然后FileInputStream 为被装饰者之一、FilterInputStream 为装饰者(缓冲类)抽象类,继承FilterInputStream的类如:BufferInputStream则是具体的装饰者之一。看到这里,这不就是没穿衣服的"装饰者设计模式"吗?赤裸裸啊。看的眼花,我们使用一张图解释IO流的整个设计体系(不仅仅是BufferInputStream)。
被装饰者
java.lang.Object
java.io.InputStream
装饰者
java.lang.Object
java.io.InputStream
java.io.FilterInputStream
不得不说, 图画的太low了。左边的为“被装饰者”,右边的为“装饰者”通过一个InputStream来维护一个具体的被装饰者。IO体系的对象绝不多数要不然就是左边的被装饰者,要不就是"装饰者" 比如: BufferInputStream缓冲类。有没发现之前学到字节流对象大部分在这里。
好,到这里,我们在学习BufferInputStream的实现就好办多了。继续。
while((length=bi.read(buf))!=-1){
System.out.println(new String(buf,0,length));
}
bi.read(buf))怎么执行的?FilterInputStream 抽象类 里面有个通用的read(byte b[])方式,就是自定义缓冲,装饰者大家通用,就写到抽象类里面最好了。那么BufferInputStream 有自己的read方式,那就override一下。
public class FilterInputStream{
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
public int read(byte b[], int off, int len) throws IOException {
return in.read(b, off, len);
}
}
public class BufferInputStream extends FilterInputStream{
public synchronized int read(byte b[], int off, int len)
throws IOException
{
getBufIfOpen(); // Check for closed stream
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;
// if not closed but no bytes available, return
InputStream input = in;
if (input != null && input.available() <= 0)
return n;
}
}
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;
}
}
BufferInputStream 不容易明白, 其实代码是这样子的。在 read(byte b[], int off, int len) 的时候一次性读取8k的数据量缓冲到buf[], 然后操作对象变成BufferInputStream 属性buf[],你自己传进来byte[] 数组就是从buf[]数组中拿, 注意这个过程建立流通道读取数据就那么一次(直接读取8k)。假如数据大于8k,那么再从设备中取数据到buf[]中,缓冲起来,接着过程如上。
看到这里,你思考一下,读取效率是不是比较高了。
总结: 你可以通过装饰者设计模式来学习IO流体系,查看具体源码来查看方法如read(byte b[], int off, int len)的具体实现!