Java IO流以及装饰器模式在其上的运用
流概述
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中还有许多其它设计模式的体现,比如说监听者模式等等。
Java IO流以及装饰器模式在其上的运用的更多相关文章
- java进阶系列之装饰器模式
		
1.介绍 装饰器模式顾名思义就是装饰某个对象的,是一种结构型模式.装饰器模式允许向一个现有对象添加新的功能,同时不改变其结构,用户可以随意的扩展原有的对象.它是作为现有的类的一个包装.装饰器模式一方面 ...
 - 重学 Java 设计模式:实战装饰器模式(SSO单点登录功能扩展,增加拦截用户访问方法范围场景)
		
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 对于代码你有编程感觉吗 很多人写代码往往是没有编程感觉的,也就是除了可以把功能按照固 ...
 - Java设计模式12:装饰器模式
		
装饰器模式 装饰器模式又称为包装(Wrapper)模式.装饰器模式以多客户端透明的方式扩展对象的功能,是继承关系的一个替代方案. 装饰器模式的结构 通常给对象添加功能,要么直接修改对象添加相应的功能, ...
 - IO流与装饰者模式
		
java使用IO流来处理不同设备之间数据的交互;所有的IO操作实际上都是对 Stream 的操作 从功能上划分: 输入流: 当数据从源进入的编写的程序时,称它为输入流; 输出流: 从程序输出回另一个源 ...
 - Java设计模式之装饰器模式
		
1.装饰器模式的定义(保持接口,扩展功能) Decorate装饰器,顾名思义,就是动态的给一个对象添加一些额外的职责,就好比对房子进行装修一样. 2.装饰器模式的特征 具有一个装饰对象. 必须拥有与被 ...
 - Java设计模式系列-装饰器模式
		
原创文章,转载请标注出处:<Java设计模式系列-装饰器模式> 一.概述 装饰器模式作用是针对目标方法进行增强,提供新的功能或者额外的功能. 不同于适配器模式和桥接模式,装饰器模式涉及的是 ...
 - java IO流详解
		
流的概念和作用 学习Java IO,不得不提到的就是JavaIO流. 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输 ...
 - Java IO流学习总结
		
Java流操作有关的类或接口: Java流类图结构: 流的概念和作用 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输 ...
 - Java IO流详尽解析
		
流的概念和作用 学习Java IO,不得不提到的就是JavaIO流. 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输 ...
 
随机推荐
- Two ways to create file using 'doc'.
			
Here are two ways to create file using 'doc' command: Method i: copy con [file name][enter key] [con ...
 - 安装Visual Studio 2010时提示"The location specified for the help content store is invalid or you do not have access to it".
			
运行注册表: (运行->输入"regedit").在 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Help\v1.0中,删除"Loc ...
 - tomcat发布项目时,空文件夹未发布成功
			
问题背景: 项目发布到服务器时,缺少文件夹,到时向此文件夹写数据时发生错误. 后来经查,缺少这个文件夹,项目部署发布时,并不会把空文件夹发布上去 解决: 1.在空文件中加入,一个文件.就可以发布成功 ...
 - hdoj 2057
			
代码: #include <stdio.h>#include<string.h>int main(){ __int64 a,b,c; while(scanf("%I6 ...
 - 寒假挑战PythonTip(一人一python)总结——算法是程序的灵魂,程序员的心法
			
2014年2月中旬,我上升到挑战python英雄榜第3名.这是我寒假修炼算法的成果之一.来一下总结吧! Linux的创始人Linus Torvalds在一次演讲中有一段涉及“什么才是优秀程序员 ...
 - Windows手动搭建PHP运行环境
			
首先~可以先在目录里面创建一个wamp目录,我的创建在 E: 盘 1.0 下载Apache2.4,x64位.VC11组件[电脑多少位装多少位] apache下载地址:https://www.apach ...
 - myisam和innodb区别
			
InnoDB MyIsam 事务 支持 不支持 锁 行锁 表锁 索引 B+树,数据和索引在一个文件中,必须有主键,如果不指定,会自动生成一个隐藏字段作 ...
 - 原生Js获取某个节点后面的第一个标签
			
nextSlbling属性 获取某个节点后面的第一个节点(可能是标签 文本) 判断获取的节点是否为标签节点还是文本节点 window.onload=function(){ var pagecount= ...
 - Android各种访问权限Permission详解
			
原文:http://jingyan.baidu.com/article/afd8f4de4688af34e386e976.html 在Android的设计中,资源的访问或者网络连接,要得到这些服务都需 ...
 - 一个简单的多线程Python爬虫(一)
			
一个简单的多线程Python爬虫 最近想要抓取拉勾网的数据,最开始是使用Scrapy的,但是遇到了下面两个问题: 前端页面是用JS模板引擎生成的 接口主要是用POST提交参数的 目前不会处理使用JS模 ...