前言
Java里面的I/O这一部分看过很多遍,每次看完之后特别混乱,又是输入流,又是输出流,又是字符流,又是字节流,还有什么过滤流,缓冲流。每次看得我如入云里雾里,直到后面看了设计模式这一块,才算真正的对Java I/O这一块有了整体的了解,理解起Java流也就容易许多。这篇博客先介绍装饰者模式,然后结合Java流看看装饰者模式如何应用到Java流中的。
装饰者模式
装饰者模式:动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。在如下三种情况下可以选择使用装饰者模式。
- 1)需要扩展一个类的功能,或给一个类增加附加责任。
- 2)需要动态的给一个对象增加功能,这些功能可以再动态地撤销。
- 3)需要增加一些基本功能的排列组合而产生的非常大量的功能,从而使继承变得 不现实。
装饰者角色结构如下图所示:
由上图可知在装饰者模式中一共有两个角色,一个是被装饰者,一个是装饰者。装饰者类和被装饰者类有共同的超类。在具体装饰者类上面还有一个抽象装饰者类。抽象装饰者类的主要功能是强制具体装饰者类实现对被装饰者类增加的功能。装饰者的类图如下:
装饰者模式实例
现在有一杯白开水(WaterClass),想向白开水里面加茶叶(TeaClass),加点糖(SugarClass),或者加点盐(SaltClass)。也许会认为那还不简单吗,加茶叶的白开水就再生成一个(WaterWithTeaClass),加糖的白开水就再生成一个(WaterWithSugarClass)这样不就行了吗?但是如果我们既要加糖又要加茶叶,或者加盐加糖这样的组合会很多,可能就要生成很多的类,造成类泛滥。这时候就要用到装饰者模式了。下面看看装饰者模式是如何轻松解决这个问题的。
WaterComponent:(被装饰者)
public class WaterComponent implements DrinkComponent{ @Override public void operation() { // TODO Auto-generated method stub System.out.print("water drink"); } }
SugarDecorator:(装饰者)
public class SugarDecorator extends DrinkDecorator{ public SugarDecorator(DrinkComponent component) { super(component); // TODO Auto-generated constructor stub } public void operation() { component.operation(); System.out.print(",with sugar"); } }
TeaDecorator:(装饰者)
public class TeaDecorator extends DrinkDecorator{ public TeaDecorator(DrinkComponent component) { super(component); // TODO Auto-generated constructor stub } public void operation() { component.operation(); System.out.print(",with Tea"); } }
MainClass
public class MainClass { public static void main(String[] args) { // TODO Auto-generated method stub WaterComponent water=new WaterComponent(); SugarDecorator sugar=new SugarDecorator(water); //往白开水里面加糖。 TeaDecorator tea=new TeaDecorator(sugar); //往加糖的白开水里面加茶叶。 tea.operation(); /*一杯有茶叶和糖的白开水就这样产生了,你也可以自由的往白开水里面加自己喜欢的东西,而并不造成类泛滥只需要继承DrinkDecorator就行*/ } }
由这个实例应该对装饰者模式有了了解,下面就来看看装饰者模式是如何应用到Java I/O中来的。
装饰者模式与Java I/O
先用一幅图来看看Java I/O到底是什么。下面的这幅图生动的刻画了Java I/O的作用。
由上图可知在Java中应用程序通过输入流(InputStream)的Read方法从源地址处读取字节,然后通过输出流(OutputStream)的Write方法将流写入到目的地址。流的来源主要有三种:
- ①本地的文件(File)
- ②控制台
- ③通过socket实现的网络通信
下面的图可以看出Java中的装饰者类和被装饰者类以及它们之间的关系,这里只列出了InputStream中的关系。
由上图可以看出只要继承了FilterInputStream的类就是装饰者类,可以用于包装其他的流,装饰者类还可以对装饰者和类进行再包装。下面看一下Java中包装流的实例:
import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.FileInputStream; import java.io.IOException; public class StreamDemo { public static void main(String[] args) throws IOException{ DataInputStream in=new DataInputStream(new BufferedInputStream(new FileInputStream("D:\JAVAworkspace\ProgramTest\src\StreamDemo.java"))); while(in.available()!=0) { System.out.print((char)in.readByte()); } in.close(); } }
上面程序中对流进行了两次包装,先用 BufferedInputStream将FileInputStream包装成缓冲流也就是给FileInputStream增加缓冲功能,再DataInputStream进一步包装方便数据处理。
实现自己的包装流
了解了装饰者模式以及在Java中的应用,下面就来实现一个自己的包装流,要实现装饰者类,按照上面的图必须先得实现FilterInputStream装饰者抽象类。来实现这样一个操作的装饰者类:将输入流中的所有小写字母变成大写字母。
import java.io.FileInputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; public class UpperCaseInputStream extends FilterInputStream { protected UpperCaseInputStream(InputStream in) { super(in); } @Override public int read() throws IOException { int c=super.read(); return (c==-1?c:Character.toUpperCase(c)); } @Override public int read(byte[] b, int off, int len) throws IOException { int result=super.read(b, off, len); for(int i=off;i<off+result;i++) { b[i]=(byte)Character.toUpperCase((char)b[i]); } return result; } public static void main(String[] args) throws IOException { int c; InputStream in=new UpperCaseInputStream(new FileInputStream("D:\JAVAworkspace\ProgramTest\src\StreamDemo.java")); try { while((c=in.read())>=0) { System.out.print((char)c); } } finally{ in.close(); } } }
输出结果:
IMPORT JAVA.IO.BUFFEREDINPUTSTREAM; IMPORT JAVA.IO.DATAINPUTSTREAM; IMPORT JAVA.IO.FILEINPUTSTREAM; IMPORT JAVA.IO.IOEXCEPTION; PUBLIC CLASS STREAMDEMO { PUBLIC STATIC VOID MAIN(STRING[] ARGS) THROWS IOEXCEPTION{ DATAINPUTSTREAM IN=NEW DATAINPUTSTREAM(NEW BUFFEREDINPUTSTREAM(NEW FILEINPUTSTREAM("D:\JAVAWORKSPACE\PROGRAMTEST\SRC\STREAMDEMO.JAVA"))); WHILE(IN.AVAILABLE()!=0) { SYSTEM.OUT.PRINT((CHAR)IN.READBYTE()); } IN.CLOSE(); } }
参考文献
1.java编程思想(Thinking In Java)
2.Head First 设计模式
3.https://www.javatpoint.com/java-io
原文:https://blog.csdn.net/u013309870/article/details/75735676