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中还有许多其它设计模式的体现,好比说监听者模式等等。

相关文章
相关标签/搜索