- 浏览: 332296 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
327764984:
CommonSql里不知道把PreparedStatment和 ...
Connection.setAutoCommit使用的注意事项 -
netwelfare:
文章讲解的不详细啊,很多东西没有讲解出来。看到这篇文章讲解的挺 ...
java字符串的拆分 lang.String.split -
iceage1980:
博主你好 我用了你这个代码 为什么总是在 if (this. ...
MD5 码 校 验 -
wisdomtl:
最后一段代码有写小的错误最后一个具体工厂的实现应该返回Cowb ...
JAVA设计模式之工厂模式 -
jateide:
网上随便转载的东西,自己都没有经过使用,好意思发出来,而且还不 ...
正则表达式中后向引用、零宽断言、负向零宽断言的解释用法
我想任何一本介绍模式的书在讲到Decorator模式的时候不能不提到它的实际应用——在Java/IO库里面的应用,<<Java与模式>>这本书也不例外,有点不一样的是,这本书在介绍的时候有个专题,是从两个模式来看Java/IO库,完这个专题后,个人感觉对Java/IO库有了全新的认识同时也加深了Decorator模式跟Adapter适配器模式的理解,现和大家分享下这个在我看来很伟大的成果,同时说明下,以下大部分文字跟图片是来自<<Java与模式>>这本书。
一。引子(概括地介绍Java的IO)
无论是哪种编程语言,输入跟输出都是重要的一部分,Java也不例外,而且Java将输入/输出的功能和使用范畴做了很大的扩充。它采用了流的机制来实现输入/输出,所谓流,就是数据的有序排列,而流可以是从某个源(称为流源或Source of Stream)出来,到某个目的地(称为流汇或Sink of Stream)去的。由流的方向,可以分成输入流和输出流,一个程序从输入流读取数据向输出流写数据。
如,一个程序可以用FileInputStream类从一个磁盘文件读取数据,如下图所示:
像FileInputStream这样的处理器叫做流处理器,它就像流的管道一样,从一个流源吸入某种类型的数据,并输出某种类型的数据。上面这种示意图叫做流的管道图。
同样道理,也可以用FileOutputStream类向一个磁盘文件写数据,如下图所示:
在实际应用这种机制并不没有太大的用处,程序需要写出地通常是非常结构化的信息,因此这些byte类型的数据实际上是一些数值,文字,源代码等。Java的I/O库提供了一个称做链接(Chaining)的机制,可以将一个流处理器跟另一个流处理器首尾相接,以其中之一的输出为输入,形成一个流管道的链接。
例如,DataInputStream流处理器可以把FileInputStream流对象的输出当作输入,将Byte类型的数据转换成Java的原始类型和String类型的数据。如下图所示:
类似地,向一个文件写入Byte类型的数据不是一个简单的过程。一个程序需要向一个文件里写入的数据往往都是结构化的,而Byte类型则是原始类型。因此在写的时候必须经过转换。DataOutputStream流处理器提供了接收了原始数据类型和String数据类型,而这个流处理器的输出数据则是Byte类型。也就是说DataOutputStream可以将源数据转换成Byte类型的数据,再输出来。
这样一来,就可以将DataOutputStream与FileOutputStream链接起来,这样程序就可以将原始数据类型和String类型的源数据写入这个链接好的双重管道里面,达到将结构化数据写到磁盘文件里面的目的,如下图所示:
这又是链接的所发挥的大作用。
流处理器所处理的流必定都有流源,而如果将流类所处理的流源分类的话,基本可以分成两大类:
第一 数组,String,File等,这一种叫原始流源。
第二 同样类型的流用做链接流类的流源,叫链接流源。
二 Java I/O库的设计原则
Java语言的I/O库是对各种常见的流源,流汇以及处理过程的抽象化。客户端的Java程序不必知道最终的流源,流汇是磁盘上的文件还是数组等;也不必关心数据是否经过缓冲的,可否按照行号读取等处理的细节。
书中提到了,对于第一次见到Java/IO库的人,无不因为这个库的庞杂而感到困惑;而对于熟悉这个库的人,而又常常为这个库的设计是否得当而争论不体。书的作者提出自己的意见,要理解Java I/O这个庞大而复杂的库,关键是要掌握两个对称性跟两个设计模式模式。
Java I/O库具有两个对称性,它们分别是:
1 输入-输出对称性,比如InputStream和OutputStream各自占据Byte流的输入与输出的两个平行的等级结构的根部。而Reader和Writer各自占据Char流的输入与输出的两个平行的等级结构的根部。
2 byte-char对称,InputStream和Reader的子类分别负责Byte和Char流的输入;OutputStream和Writer的子类分别负责Byte和Char流的输出,它们分别形成平行的等级结构。
Java I/O库的两个设计模式:
Java的I/O库总体设计是符合装饰者模式(Decorator)跟适配器模式(Adapter)的。如前所述,这个库中处理流的类叫做流类。引子里所谈到的FileInputStream,FileOutputStream,DataInputStream及DataOutputStream都是流处理器的例子。
1 装饰者模式:在由InputStream,OutputStream,Reader和Writer代表的等级结构内部,有一些流处理器可以对另一些流处理器起到装饰作用,形成新的,具有改善了的功能的流处理器。装饰者模式是Java I/O库的整体设计模式。这样的一个原则是符合装饰者模式的,如下图所示:
2 适配器模式:在由InputStream,OutputStream,Reader和Writer代表的等级结构内部,有一些流处理器是对其它类型的流源的适配。这就是适配器模式的应用,如下图所示。
适配器模式应用到了原始流处理器的设计上面,构成了I/O库所有流处理器的起点。
JDK为程序员提供了大量的类库,而为了保持类库的可重用性,可扩展性和灵活性,其中使用到了大量的设计模式,本文将介绍JDK的I/O包中使用到的Decorator模式,并运用此模式,实现一个新的输出流类。
Decorator模式简介
Decorator模式又名包装器(Wrapper),它的主要用途在于给一个对象动态的添加一些额外的职责。与生成子类相比,它更具有灵活性。
有时候,我们需要为一个对象而不是整个类添加一些新的功能,比如,给一个文本区添加一个滚动条的功能。我们可以使用继承机制来实现这一功能,但是这种方法不够灵活,我们无法控制文本区加滚动条的方式和时机。而且当文本区需要添加更多的功能时,比如边框等,需要创建新的类,而当需要组合使用这些功能时无疑将会引起类的爆炸。
我们可以使用一种更为灵活的方法,就是把文本区嵌入到滚动条中。而这个滚动条的类就相当于对文本区的一个装饰。这个装饰(滚动条)必须与被装饰的组件(文本区)继承自同一个接口,这样,用户就不必关心装饰的实现,因为这对他们来说是透明的。装饰会将用户的请求转发给相应的组件(即调用相关的方法),并可能在转发的前后做一些额外的动作(如添加滚动条)。通过这种方法,我们可以根据组合对文本区嵌套不同的装饰,从而添加任意多的功能。这种动态的对对象添加功能的方法不会引起类的爆炸,也具有了更多的灵活性。
以上的方法就是Decorator模式,它通过给对象添加装饰来动态的添加新的功能。如下是Decorator模式的UML图:
Component为组件和装饰的公共父类,它定义了子类必须实现的方法。
ConcreteComponent是一个具体的组件类,可以通过给它添加装饰来增加新的功能。
Decorator是所有装饰的公共父类,它定义了所有装饰必须实现的方法,同时,它还保存了一个对于Component的引用,以便将用户的请求转发给Component,并可能在转发请求前后执行一些附加的动作。
ConcreteDecoratorA和ConcreteDecoratorB是具体的装饰,可以使用它们来装饰具体的Component.
JAVA IO包中的Decorator模式
JDK提供的java.io包中使用了Decorator模式来实现对各种输入输出流的封装。以下将以java.io.OutputStream及其子类为例,讨论一下Decorator模式在IO中的使用。
首先来看一段用来创建IO流的代码:
以下是代码片段:
try {
OutputStream out = new DataOutputStream(new FileOutputStream("test.txt"));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
这段代码对于使用过JAVA输入输出流的人来说再熟悉不过了,我们使用DataOutputStream封装了一个FileOutputStream.这是一个典型的Decorator模式的使用,FileOutputStream相当于Component,DataOutputStream就是一个Decorator.将代码改成如下,将会更容易理解:
以下是代码片段:
try {
OutputStream out = new FileOutputStream("test.txt");
out = new DataOutputStream(out);
} catch(FileNotFoundException e) {
e.printStatckTrace();
}
由于FileOutputStream和DataOutputStream有公共的父类OutputStream,因此对对象的装饰对于用户来说几乎是透明的。下面就来看看OutputStream及其子类是如何构成Decorator模式的:
OutputStream是一个抽象类,它是所有输出流的公共父类,其源代码如下:
以下是代码片段:
public abstract class OutputStream implements Closeable, Flushable {
public abstract void write(int b) throws IOException;
……
}
它定义了write(int b)的抽象方法。这相当于Decorator模式中的Component类。
ByteArrayOutputStream,FileOutputStream 和 PipedOutputStream 三个类都直接从OutputStream继承,以ByteArrayOutputStream为例:
以下是代码片段:
public class ByteArrayOutputStream extends OutputStream {
protected byte buf[];
protected int count;
public ByteArrayOutputStream() {
this(32);
}
public ByteArrayOutputStream(int size) {
if (size 〈 0) {
throw new IllegalArgumentException("Negative initial size: "
+ size);
}
buf = new byte[size];
}
public synchronized void write(int b) {
int newcount = count + 1;
if (newcount 〉 buf.length) {
byte newbuf[] = new byte[Math.max(buf.length 〈〈 1, newcount)];
System.arraycopy(buf, 0, newbuf, 0, count);
buf = newbuf;
}
buf[count] = (byte)b;
count = newcount;
}
……
}
它实现了OutputStream中的write(int b)方法,因此我们可以用来创建输出流的对象,并完成特定格式的输出。它相当于Decorator模式中的ConcreteComponent类。
接着来看一下FilterOutputStream,代码如下:
以下是代码片段:
public class FilterOutputStream extends OutputStream {
protected OutputStream out;
public FilterOutputStream(OutputStream out) {
this.out = out;
}
public void write(int b) throws IOException {
out.write(b);
}
……
}
同样,它也是从OutputStream继承。但是,它的构造函数很特别,需要传递一个OutputStream的引用给它,并且它将保存对此对象的引用。而如果没有具体的OutputStream对象存在,我们将无法创建FilterOutputStream.由于out既可以是指向FilterOutputStream类型的引用,也可以是指向ByteArrayOutputStream等具体输出流类的引用,因此使用多层嵌套的方式,我们可以为ByteArrayOutputStream添加多种装饰。这个FilterOutputStream类相当于Decorator模式中的Decorator类,它的write(int b)方法只是简单的调用了传入的流的write(int b)方法,而没有做更多的处理,因此它本质上没有对流进行装饰,所以继承它的子类必须覆盖此方法,以达到装饰的目的。
BufferedOutputStream 和 DataOutputStream是FilterOutputStream的两个子类,它们相当于Decorator模式中的ConcreteDecorator,并对传入的输出流做了不同的装饰。以BufferedOutputStream类为例:
以下是代码片段:
public class BufferedOutputStream extends FilterOutputStream {
……
private void flushBuffer() throws IOException {
if (count 〉 0) {
out.write(buf, 0, count);
count = 0;
}
}
public synchronized void write(int b) throws IOException {
if (count 〉= buf.length) {
flushBuffer();
}
buf[count++] = (byte)b;
}
……
}
这个类提供了一个缓存机制,等到缓存的容量达到一定的字节数时才写入输出流。首先它继承了FilterOutputStream,并且覆盖了父类的write(int b)方法,在调用输出流写出数据前都会检查缓存是否已满,如果未满,则不写。这样就实现了对输出流对象动态的添加新功能的目的。
下面,将使用Decorator模式,为IO写一个新的输出流。
自己写一个新的输出流
了解了OutputStream及其子类的结构原理后,我们可以写一个新的输出流,来添加新的功能。这部分中将给出一个新的输出流的例子,它将过滤待输出语句中的空格符号。比如需要输出"java io OutputStream",则过滤后的输出为"javaioOutputStream".以下为SkipSpaceOutputStream类的代码:
以下是代码片段:
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* A new output stream, which will check the space character
* and won‘t write it to the output stream.
* @author Magic
*
*/
public class SkipSpaceOutputStream extends FilterOutputStream {
public SkipSpaceOutputStream(OutputStream out) {
super(out);
}
/**
* Rewrite the method in the parent class, and
* skip the space character.
*/
public void write(int b) throws IOException{
if(b!=‘ ’){
super.write(b);
}
}
}
它从FilterOutputStream继承,并且重写了它的write(int b)方法。在write(int b)方法中首先对输入字符进行了检查,如果不是空格,则输出。
以下是一个测试程序:
以下是代码片段:
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Test the SkipSpaceOutputStream.
* @author Magic
*
*/
public class Test {
public static void main(String[] args){
byte[] buffer = new byte[1024];
/**
* Create input stream from the standard input.
*/
InputStream in = new BufferedInputStream(new DataInputStream(System.in));
/**
* write to the standard output.
*/
OutputStream out = new SkipSpaceOutputStream(new DataOutputStream(System.out));
try {
System.out.println("Please input your words: ");
int n = in.read(buffer,0,buffer.length);
for(int i=0;i〈n;i++){
out.write(buffer);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
执行以上测试程序,将要求用户在console窗口中输入信息,程序将过滤掉信息中的空格,并将最后的结果输出到console窗口。比如:
以下是引用片段:
Please input your words:
a b c d e f
abcdef
总 结
在java.io包中,不仅OutputStream用到了Decorator设计模式,InputStream,Reader,Writer等都用到了此模式。而作为一个灵活的,可扩展的类库,JDK中使用了大量的设计模式,比如在Swing包中的MVC模式,RMI中的Proxy模式等等。对于JDK中模式的研究不仅能加深对于模式的理解,而且还有利于更透彻的了解类库的结构和组成。
发表评论
-
向数据库插入中文乱码【转】
2015-05-02 19:35 1391又遇到乱码问题,这个编码问题有时候真是让人头大。找了半 ... -
tomcat web.xml配置(转)
2013-03-27 15:17 1196web.xml元素介绍 我将自 ... -
java获取URL内容截取后保存到Excel中
2012-11-01 16:27 2467通过java.net 访问相应url,过滤返回的信息,并保存 ... -
java 反射
2012-10-24 14:47 1118java反射是java控制反转 ... -
POI包说明
2012-10-18 13:40 1725在线Javadoc:http://www.osctool ... -
JAVA_IO详解_包括讲解IO中各种流及其用法
2012-10-18 13:21 1023个人感觉比较详细的javaIO资料,下载附件 -
Spring入门示例及相关概念介绍(转)
2012-07-01 15:38 1190Spring入门示例及相关概念介绍 作者:http:/ ... -
servlet中init()和service()方法的区别(转)
2012-06-26 14:43 1794servlet中init()和service() ... -
getRequestDispatcher()与sendRedirect()的区别(转)
2012-06-26 14:37 10771.request.getRequestDispatch ... -
JAVA数据结构(~转~)
2012-06-26 13:45 1138JAVA数据结构 线性表,链表,哈希表是常用的数据结构 ... -
Java多线程编程总结(转)
2012-06-18 09:59 1028Java多线程编程总结 下面是Java线程系 ... -
J2EE开发常用开源框架技术(转)
2012-06-14 16:36 8061主要就我所了解的J2EE开发的框架或开源项目做个介绍,可以根据 ... -
Oracle回滚注意事项
2012-04-11 17:27 1114回滚到fisrt()时注意使用next()时需要将光标移到第一 ... -
Date 和 Calender 的转化 (转)
2012-04-09 14:23 1519Date 和 Calender 的转化 ... -
创建Java线程池(转)
2012-03-30 17:30 1365线程池的作用: 线程池作用就是限制系统中执行线 ... -
对Servlet的初始化的理解
2012-03-12 11:33 1699在读我自己的认识之前 , 我们 ... -
用java调用oracle存储过程总结
2012-02-17 14:30 01、什么是存储过程。存储过程是数据库服务器端的一段程序,它 ... -
JAVA设计模式之工厂模式
2012-02-17 11:03 13054JAVA设计模式之工厂模 ... -
Java技术----实现JAVA的动态类载入机制
2012-02-17 10:44 1658----作为充分利用Java的 ... -
工厂模式
2012-02-17 09:51 935工厂模式是我们最常用的模式了,著名的Jive论坛 ,就 ...
相关推荐
java IO教程,java IO教程,java IO教程,java IO教程java IO教程java IO教程java IO教程java IO教程,java IO教程java IO教程java IO教程java IO教程java IO教程,java IO教程,java IO教程,java IO教程,java IO...
Java教程之javaIO详解Java教程之javaIO详解Java教程之javaIO详解
以前学java时候看的马士兵老师的教程,老师讲的较快,有些不适应,自己做些笔记巩固练习,效果还不错,贴出来分析大家把,配图+示例。。。
IO流 javaio java 流
这是别人总结的很有实用价值的javaIO流教程。
java io 结构图java io 结构图
深入分析javaIO的工作机制.doc
彻底明白Java的IO流
java学习笔记1(java io/nio)设计模式
1、Java IO_NIO 2、Java+IO.pdf
java IO文档java IO文档java IO文档java IO文档java IO文档java IO文档
IO 简介,只看这篇,IO全部搞定,对于初学者来说,不可多得
JAVA课件Java IO.ppt详细了解JAVA
javaIO流javaIO流
java io.pdf java io.pdf
java io处理 方法java io处理 方法
java io 文件读写
深入分析JavaIO的工作机制.doc
java IO操作详细解释 清华大学java io专用教材
Java io流的总结