Java IO InputStream家族-装饰器模式
一、介绍
装饰器模式:当一个对象A的功能做不到某些任务时,我们可以通过新建一个新的对象B(装饰器),B继承了A,在这个B中传入A,通过这个A,在B的method()调用A的method()。在准确的需要加强某个功能的时候,可以新建一个对象C去继承B,然后在C的method()调用B的method()并添加相应需要的功能(看起来和代理模式很像)。
因为InputStream有许多子类,假如我们需要使用缓存的方式从文件中读取字节流,就需要FileInputStream、BufferedInputStream互相配合,
FileInputStream fs=new FileInputStream("某文件绝对路径");
InputStream inputStream=new BufferedInputStream(fs);
byte[] bytes=new byte[5];
while (inputStream.read(bytes)!=-1){
System.out.println(new String(bytes,0,bytes.length));
}
fs.close();
先创建一个FileInputStream对象,然后将它放入BufferedInputStream的构造函数中。
从UML图可以看出FilterInputStream继承于InputStream,BufferedInputStream继承FilterInputStream
二、介绍InputStream和其子类相关源码
此处以read()方法举例
InputStream是一个抽象类
public abstract class InputStream implements Closeable{
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
}
FilterInputStream(装饰器Decorator)继承了InputStream
public class FilterInputStream extends InputStream {
/**
* The input stream to be filtered.
* 传入InputStream对象
*/
protected volatile InputStream in;
protected FilterInputStream(InputStream in) {
this.in = in;
}
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
public int read(byte b[], int off, int len) throws IOException {
//通过in调用InputStream的read()方法
return in.read(b, off, len);
}
}
具体的装饰器角色BufferedInputStream继承FilterInputStream(装饰器)
public class BufferedInputStream extends FilterInputStream{
public BufferedInputStream(InputStream in) {
this(in, DEFAULT_BUFFER_SIZE);
}
public BufferedInputStream(InputStream in, int size) {
//进入父类的构造器,传入InputStream对象
super(in);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}
private byte[] getBufIfOpen() throws IOException {
byte[] buffer = buf;
if (buffer == null)
throw new IOException("Stream closed");
//返回一个byte[]数组
return buffer;
}
/**
* Check to make sure that underlying input stream has not been
* nulled out due to close; if not return it;
*/
private InputStream getInIfOpen() throws IOException {
//in:FilterInputStream的属性
InputStream input = in;
if (input == null)
throw new IOException("Stream closed");
//返回当前输入流
return input;
}
//加强以后的read方法
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 int read1(byte[] b, int off, int len) throws IOException {
int avail = count - pos;
if (avail <= 0) {
/* If the requested length is at least as large as the buffer, and
if there is no mark/reset activity, do not bother to copy the
bytes into the local buffer. In this way buffered streams will
cascade harmlessly. */
if (len >= getBufIfOpen().length && markpos < 0) {
return getInIfOpen().read(b, off, len);
}
fill();
avail = count - pos;
if (avail <= 0) return -1;
}
int cnt = (avail < len) ? avail : len;
/*
*将getBufIfOpen()返回的byte[]数组的pos开始复制到b中(从off中),复制长度为cnt
*/
System.arraycopy(getBufIfOpen(), pos, b, off, cnt);
pos += cnt;
return cnt;
}
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;
/**
*FilterInputStream的in属性,调用read()方法
*/
int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
if (n > 0)
count = n + pos;
}
}
三、总结
在整个通过缓存的方式从文件中读取字节流过程中,整个核心还是InputStream(不可缺少的关键),FilterInputStream相当于一个装饰器类,而BufferedInputStream相当于一个具体的装饰类。因为FilterInputStream中已经传入了InputStream in,所以BufferedInputStream可以直接调用对应的输入流和read()方法,再在read()方法中进行方法的增强(read1()),将FileInputStream和BufferedInputStream结合起来大大提高了整个文件读取的速度。这样子,我们在读取文件的时候,可以在不修改InputStream源码的情况下,对功能进行扩展。
注:在网上看了挺多关于装饰器的文章,推荐一篇自以为是比较好的参考链接