1. 概念
FilterInputStream和FilteOutputStream分别是过滤输入流和过滤输出流,他们的作用是为基础流提供一些额外的功能.拿FilterInputStream来说,常见的子类有DataInputStream,BufferedInputStream以及PushBackInputStream等.其子类总结为:
- DataInputStream.数据输入流,以机器无关的方式读取Java的基本类型.
- BufferedInputStream缓冲输入流,由于基础输入流一个字节一个字节读取,频繁与磁盘进行交互,造成读取速度较低.缓冲流的存在就是先将数据读取到缓冲流(内存中),然后一次性从内存中读取多个字符.提高读取的效率.
- PushBackInputStream回退输入流,java中读取数据的方式是顺序读取,如果某个数据不需要读取,需要程序处理.PushBackInputStream就可以将某些不需要的数据回退到缓冲中.
1. 思考
FilterInputStream所有的子类都是为基础流输入提供了一些额外的功能,为什么不直接继承基础流,将基础流中的方法直进行"装饰".这里要说到一个装饰者模式和继承的区别:
- 装饰者模式,就是将原有的基础流进行"装饰",那么装饰后的方法要与原先被装饰的基础类要保持一致,也可以在对基础流进行扩展.而继承是继承父类的属性和方法,通过重写父类里面的方法也可以起到"装饰"作用.比如强化或者优化父类里面的一些方法.两者的区别是装饰者模式可以动态地扩展一个对象.给对象添加额外的功能.而且装饰者和被装饰者之间不会产生耦合
- 先看一下继承,例如要实现DataInputStream的功能,我们需要继承每个基础输入流,而且基础输入流不仅仅是图中这些,还有FileInputStream所有子类以及InputStream子类等等类,都需要继承,那么这些类就会爆炸式的增长.而且类之间耦合性特别高.如果使用继承,那么IO流系统何其庞大
- 装饰者模式存在意义就在此处,相比继承,没有这么多繁杂的类,而且类与类的之间的耦合性有所降低,具体做法就是提出一个公共类FilterInputStream.而其子类就是各个功能的实现类.如果想要使用基础输入流的某个功能,那么就可以将对应的基础输入流传到对应的子类构造方法中.代码也是来源于生活,这个装饰者模式跟生活中很多实例相似,比如每家有可乐,要想喝冰可乐,怎么办.不是每家去买一个冰箱,而是大家凑钱买一个公共的冰箱,然后要想喝冰颗可乐,就直接放在公共的冰箱里面,就实现了"冰"这个功能(好吧这个例子有点牵强)
- 至于如何使用FilterInputStream和FilterOutputStream呢?简单的拿FilterInputStream来说就是写一个新子类继承FilterInputStream,这个新的子类有个构造函数是传一个inpustream对象进去的(这个对象就是要添加功能的基础流对象,如:FileInputStream对象)。由于FilterInputStream也是InputStream的子类。因而我们在新的子类里面通过重写从InputStream继承来的read(),close()等方法来添加功能,这样就可以实现了我们要给基础流的读写添加功能的要求。在java中,BufferedInputStream,ZipInputStream等类就是这样使用的例子,具体这两个是提供了基础流输入时的缓冲和读取压缩文件并解压的功能,java还有很多类似的类,他们都是通过装饰者模式给基础输入输出流添加功能
如:现在要用FileInputStream实现一个功能,读取与机器无关的java类中Int类型.就可以直接将FileInputStream传入DataInputStream的有参构造方法中public class FilterDemo { public static void main(String[] args) throws Exception{ DataOutputStream dos = new DataOutputStream(new FileOutputStream("D:\\java.txt")); dos.writeInt(1234567); DataInputStream dis = new DataInputStream(new FileInputStream("D:\\java.txt")); System.out.println(dis.readInt()); dis.close(); dos.close(); } } //结果 1234567
2. 源码
public class FilterInputStream extends InputStream {
protected volatile InputStream in;
//有参构造方法,传入基础输入流
protected FilterInputStream(InputStream in) {
this.in = in;
}
//读取一个字节
public int read() throws IOException {
return in.read();
}
//从输入流读取数据到字节数组b中
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
//从输入流中读取数据到b的off位置开始,len个字节.
public int read(byte b[], int off, int len) throws IOException {
return in.read(b, off, len);
}
//跳过n个字节
public long skip(long n) throws IOException {
return in.skip(n);
}
//从输入流中可读取的剩余字节数
public int available() throws IOException {
return in.available();
}
public void close() throws IOException {
in.close();
}
//在输入流当前标记
public synchronized void mark(int readlimit) {
in.mark(readlimit);
}
//将当前位置重置到标记的位置
public synchronized void reset() throws IOException {
in.reset();
}
//测试输入流是否支持标记
public boolean markSupported() {
return in.markSupported();
}
}
public class FilterOutputStream extends OutputStream {
protected OutputStream out;
//有参构造方法,传入基础输出流
public FilterOutputStream(OutputStream out) {
this.out = out;
}
//将b写到输出流
public void write(int b) throws IOException {
out.write(b);
}
//将字节数组b写到输出流中
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
//将字节数组b中off开始,len个字节写到输出流中
public void write(byte b[], int off, int len) throws IOException {
if ((off | len | (b.length - (len + off)) | (off + len)) < 0)
throw new IndexOutOfBoundsException();
for (int i = 0 ; i < len ; i++) {
write(b[off + i]);
}
}
//刷新输出流
public void flush() throws IOException {
out.flush();
}
//刷新输出流,强制将已写入缓冲的流写出
@SuppressWarnings("try")
public void close() throws IOException {
try (OutputStream ostream = out) {
flush();
}
}
}