由裝飾者模式來深入理解Java I/O整體框架
前言
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