您好,登錄后才能下訂單哦!
這篇文章主要講解了“如何從Java IO源碼看裝飾者模式的用法”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“如何從Java IO源碼看裝飾者模式的用法”吧!
設計模式(design pattern)是對軟件設計中普遍存在(反復出現)的各種問題,所提出的解決方案。
我們在做業務開發的時候,要對業務進行抽象,抽象就是在理解了業務之后再給出一個定義,一個概念。所以概念是從實際場景反推出來的,而不是先有概念。設計模式既然是對問題的總結,自然符合先有場景再有概念。
要理解設計模式就要到具體的場景中去,事實證明,網上流傳的諸多書籍或文章舉出的例子,都很勉強。
眾所周期,Java IO體系使用了裝飾者模式,如果不理解這個模式,就很難熟練的使用io相關的API,不如直接到最經典的例子中去,直接看Java io源碼設計,既能理解模式的使用方式,又能熟悉Java IO的用法和體系!
public static void main(String[] args) throws IOException { File file = new File("file.txt");//文件內容為:abcdefgh InputStream in = new BufferedInputStream( new FileInputStream(file), 512); int firstByte = in.read(); System.out.println(firstByte); int secondByte = in.read(); System.out.println(secondByte); } //輸出結果:97,98
這段代碼的意思是:使用FileInputStream
從文件中讀取數據,使用BufferedInputStream
包裝數據,并且分配一個512字節大小的緩沖區。使用 in.read()
讀取一個字節的數據。當讀取第一個字節的時候,因為緩沖區沒有內容,所以會調用FileInputStream
從文件中讀取512個字節放入內存緩沖區,當讀取第二個字節的時候,因為緩存區已經有數據,所以直接返回。如果不使用BufferedInputStream
,則每次都要到文件中讀取一個字節,效率低,這就是使用BufferedInputStream
做緩沖的意義。
這里為什么輸出97?文件中使用utf-8編碼,使用不定長的1-4個字節來表示一個字符,對于ascii里面的字符只使用一個字節,所以文件中第一個字節即字符a的utf-8的編碼,也等于ascii編碼,換算成十進制即97。
下面是BufferedInputStream
的構造方法:
public BufferedInputStream(InputStream in, int size) { super(in); if (size <= 0) { throw new IllegalArgumentException("Buffer size <= 0"); } buf = new byte[size]; }
下面是BufferedInputStream
的read方法:這里判斷pos與count的比較,count是緩沖區中的有效字節數,pos即當前讀取位置,構造方法只是初始化一個字節數組,里面還沒有有效數據,所以第一次讀取時count為0,pos為0,這時候調用fill方法填充緩存區數據。
public synchronized int read() throws IOException { if (pos >= count) { fill(); if (pos >= count) return -1; } return getBufIfOpen()[pos++] & 0xff; }
下面是BufferedInputStream
的fill方法,刪減一部分不必要的內容,這里的buffer
就是初始化的緩沖區,其調用了getInIfOpen方法將數據寫入緩沖區。
private void fill() throws IOException { byte[] buffer = getBufIfOpen(); count = pos; int n = getInIfOpen().read(buffer, pos, buffer.length - pos); if (n > 0) count = n + pos; }
getInIfOpen方法使用in
這個對象,這個in
即構造方法傳入的FileInputStream
,所以就相當于調用FileInputStream
讀取512字節的數據寫入到BufferedInputStream
的緩沖區。
private InputStream getInIfOpen() throws IOException { InputStream input = in; if (input == null) throw new IOException("Stream closed"); return input; }
可以看到BufferedInputStream
起到的是一個提供緩沖區的增強功能。那么還有沒有別的增強功能?
public static void main(String[] args) throws IOException { File file = new File("file.txt");//文件內容:abcde DataInputStream in = new DataInputStream(new BufferedInputStream( new FileInputStream(file), 512)); char c = in.readChar(); System.out.println(c); }//輸出結果:慢
這里使用DataInputStream
繼續包裝BufferedInputStream
,DataInputStream
從基礎輸入流中讀取原始Java數據類型,比如這里使用readChar方法讀取第一個字符,但是為什么會輸出慢
這個字?查看源碼:
public final char readChar() throws IOException { int ch2 = in.read(); //97 int ch3 = in.read();//98 if ((ch2 | ch3) < 0) throw new EOFException(); return (char)((ch2 << 8) + (ch3 << 0)); }
因為Java中的char類型使用2個字節來存儲,所以這里讀取連續的兩個字節來當做一個字符。由于文件中存儲的是字母,前面已經解釋了,ch2的值應該是97,ch3的值是98,執行(ch2 << 8) + (ch3 << 0)的結果是十進制的24930
,而24930
在utf8中對應的就是漢字慢
。這里只是解釋原理,不涉及如何正確讀取原始數據的問題。
在這個例子當中,DataInputStream
和BufferedInputStream
用于增強功能,也就是裝飾者角色,而FileInputStream
是數據源,是被裝飾者,他們都有著共同的方法,并且裝飾者要持有一個被裝飾者或者其他裝飾者的引用(這里通過構造方法注入),所以他們應當有共同的父類。這些裝飾器可以自由組合,以實現不同的功能。所以,如果不這樣做的話,還有更好的實現方式來達到同樣的效果嗎?完整的繼承關系如下圖:
首先有個被裝飾對象
然后有一堆的裝飾器用于增強不同的功能
裝飾器與被裝飾對象具有相同的行為(超類中的抽象方法)
裝飾器應當持有被裝飾者的引用或者其他裝飾者(持有超類的引用,多態的體現)
組合這些裝飾器和被裝飾對象(構造方法注入)
感謝各位的閱讀,以上就是“如何從Java IO源碼看裝飾者模式的用法”的內容了,經過本文的學習后,相信大家對如何從Java IO源碼看裝飾者模式的用法這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。