JavaIO流以及装饰器模式在其上的运用
2016-01-07 08:30:12 | 来源:玩转帮会 | 投稿:佚名 | 编辑:小柯

原标题:JavaIO流以及装饰器模式在其上的运用

流概述

Java中,流是一种有序的字节序列,可以有任意的长度。从应用流向目的地称为输出流,从目的地流向应用称为输入流。

Java的流族谱

Java的 java.io 包中囊括了整个流的家族,输出流和输入流的谱系如下所示:

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,后关闭。

在输入流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() 方法对其进行关闭。

装饰器模式

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

这种设计模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

以InputStream为例,它是一个抽象类:

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

并定义有抽象方法

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

tags:

上一篇  下一篇

相关:

GradleforAndroid第三篇(依赖管理)

依赖管理依赖管理是Gradle最闪耀的地方,最好的情景是,你仅仅只需添加一行代码在你的build文件,Gradle会自

GradleforAndroid第四篇(构建变体)

当你在开发一个app,通常你会有几个版本。大多数情况是你需要一个开发版本,用来测试app和弄清它的质量,然后

GradleforAndroid第五篇(多模块构建)

Android studio不仅允许你为你的app和依赖库创建模块,同时也可为Android wear,Android TV,Google App En

Android程序员必须掌握的三种自动化测试方法

在日常的开发中,尤其是app开发,因为不像web端那样 出错以后可以热更新,所以app开发 一般对软件质量有更高

最全面的2015年全球程序员研究报告

前言开发者调查是Stack Overflow 每年都要进行的一次开发者用户调查问卷活动,调查对象为在 Stack Overflow

杨佩洁30岁庆生 许愿使坏

言言(左)帮杨佩洁庆生。(鸿言娱乐提供) 导演叶天伦(右)及言言(左起)帮杨佩洁庆生。(鸿言娱乐提供) 演员杨

2015年最流行的10个Linux发行版

本文由玩赚乐(www.banghui.org)– 蒋丽丽原创翻译,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划

方舲穿神V内衣吸爆眼球 走秀惊魂糗Hold不住Nu Bra

方舲穿粉色内衣加小短裤,诠释运动时也可以很性感。(张祐铭摄) 方舲练肌健健美。(取材自脸书) 伊林娱

廖芳洁《十点不一样》10年有成

廖芳洁在播报前习惯先分析新闻,并亲自撰写每则新闻稿头导言。 TVBS《十点不一样》播出届满10年,除培养出

Android图片压缩实现过程及代码

android图片压缩无非两种,一种质量压缩,一种像素压缩,前者多用于图片上传时,后者多用于本地图片展示缩略

站长推荐: