Java IO : 流,以及裝飾器模式在其上的運用

流概述

Java中,流是一種有序的字節序列,能夠有任意的長度。從應用流向目的地稱爲輸出流,從目的地流向應用稱爲輸入流。java

Java的流族譜

Java的java.io包中囊括了整個流的家族,輸出流和輸入流的譜系以下所示:
bVrZe4
bVrZe5設計模式

InputStream和OutputStream

InputStream和OutputStream分別是輸入輸出流的頂級抽象父類,只定義了一些抽象方法供子類實現。數組

在輸出流OutputStream中,若是你須要向一個輸出流寫入數據,能夠調用void write(int b)方法,這個方法會將b的低八位寫入流中,高24位將會被自動忽略。若是想要批量寫入數據呢,那麼能夠調用void write(byte[] b) 方法將一個字節數組的內容所有寫入流中,同時還有void write(byte[] b, int off, int len)可讓你指定從哪裏寫入多少數據。
若是你但願你向流中寫入的數據可以儘快地輸送到目的地,好比說文件,那麼能夠在寫入數據後,調用flush()方法將當前輸出流刷到操做系統層面的緩衝區中。不過須要注意的是,此方法並不保證數據立馬就能刷到實際的物理目的地(好比說存儲)。
使用完流後應該調用其close()方法將流關閉,流關閉時,將會先flush,後關閉。ide

在輸入流InputStream中,能夠經過int read() 方法來從流中讀取一個字節,批量讀取字節能夠經過int read(byte[] b)或者int read(byte[] b, int off, int len)來實現,這兩個方法的返回值爲實際讀取到的字節數。若是須要重複讀取流中某段數據,能夠在讀取以前先使用void mark(int readlimit)方法在當前位置作一個記號,以後經過void reset()方法返回到以前作標記的位置,不過在作這些標記操做以前,須要先經過boolean markSupported()方法來肯定該流是否支持標記。若是對某些可預知的數據不感興趣,可使用long skip(long n)來調過一些流中的一些數據。性能

使用完流,不管是輸入仍是輸出流,都要調用其close()方法對其進行關閉。this

裝飾器模式

裝飾器模式(Decorator Pattern)容許向一個現有的對象添加新的功能,同時又不改變其結構。這種類型的設計模式屬於結構型模式,它是做爲現有的類的一個包裝。spa

這種設計模式建立了一個裝飾類,用來包裝原有的類,並在保持類方法簽名完整性的前提下,提供了額外的功能。操作系統

以InputStream爲例,它是一個抽象類:設計

public abstract class InputStream implements Closeable {
    ...
    ...
}

並定義有抽象方法3d

public abstract int read() throws IOException;

該抽象方法由具體的子類去實現,經過InputStream的族譜圖能夠看到,直接繼承了InputStream,而且提供某一特定功能的子類有:

  • ByteArrayInputStream

  • FileInputStream

  • ObjectInputStream

  • PipedInputStream

  • SequenceInputStream

  • StringBufferInputStream

這些子類都具備特定的功能,好比說,FileInputStream表明一個文件輸入流並提供讀取文件內容的功能,ObjectInputStream提供了對象反序列化的功能。

InputStream這個抽象類有一個子類與上述其它子類很是不一樣,這個子類就是FilterInputStream,可參見上圖中的InputStream族譜圖。

翻開FilterInputStream的代碼,咱們能夠看到,它內部又維護了一個InputStream的成員對象,而且它的全部方法,都是調用這個成員對象的同名方法。
換句話說,FilterInputStream它什麼事都不作。就是把調用委託給內部的InputStream成員對象。以下所示:

public class FilterInputStream extends InputStream {
    protected volatile InputStream in;
    
    protected FilterInputStream(InputStream in) {
        this.in = in;
    }
    
    public int read() throws IOException {
        return in.read();
    }
    
    public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
    }
    
    public int read(byte b[], int off, int len) throws IOException {
        return in.read(b, off, len);
    }
    
    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();
    }

FilterInputStream的又有其子類,分別是:

  • BufferedInputStream

  • DataInputStream

  • LineNumberInputStream

  • PushbackInputStream

雖然從上面代碼看FilterInputStream並無作什麼有卵用的事,可是它的子類可不一樣了,以BufferedInputStream爲例,這個類提供了提早讀取數據的功能,也就是緩衝的功能。能夠看看它的read方法:

    public synchronized int read() throws IOException {        if (pos >= count) {
            fill();            if (pos >= count)                return -1;
        }        return getBufIfOpen()[pos++] & 0xff;
    }

能夠看到,當pos>=count時,意即須要提早緩衝一些數據的時候到了,那麼就會調用fill()將緩衝區加滿,以便後續讀取。因爲本文只討論io流的裝飾器模式,因此關於具體實現細節將不會展開討論,好比本文不會討論fill()方法是如何實現的,在這裏能夠先將它當作一個黑盒子。

從這裏能夠開始感覺到,BufferedInputStream就是一個裝飾者,它能爲一個本來沒有緩衝功能的InputStream添加上緩衝的功能。

好比咱們經常使用的FileInputStream,它並無緩衝功能,咱們每次調用read,都會向操做系統發起調用索要數據。假如咱們經過BufferedInputStream來裝飾它,那麼每次調用read,會預先向操做系統多拿一些數據,這樣就不知不覺中提升了程序的性能。如如下代碼所示:

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("/home/user/abc.txt")));

同理,對於其它的FilterInputStream的子類,其做用也是同樣的,那就是裝飾一個InputStream,爲它添加它本來不具備的功能。OutputStream以及家眷對於裝飾器模式的體現,也以此類推。

JDK中的io流的設計是設計模式中裝飾器模式的一個經典示範,若是細心發現,JDK中還有許多其它設計模式的體現,好比說監聽者模式等等。

相關文章
相關標籤/搜索