前言

Java里面的I/O这一部分看过很多遍,每次看完之后特别混乱,又是输入流,又是输出流,又是字符流,又是字节流,还有什么过滤流,缓冲流。每次看得我如入云里雾里,直到后面看了设计模式这一块,才算真正的对Java I/O这一块有了整体的了解,理解起Java流也就容易许多。这篇博客先介绍装饰者模式,然后结合Java流看看装饰者模式如何应用到Java流中的。

装饰者模式

装饰者模式:动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。在如下三种情况下可以选择使用装饰者模式。

  • 1)需要扩展一个类的功能,或给一个类增加附加责任。
  • 2)需要动态的给一个对象增加功能,这些功能可以再动态地撤销。
  • 3)需要增加一些基本功能的排列组合而产生的非常大量的功能,从而使继承变得 不现实。

装饰者角色结构如下图所示:

由上图可知在装饰者模式中一共有两个角色,一个是被装饰者,一个是装饰者。装饰者类和被装饰者类有共同的超类。在具体装饰者类上面还有一个抽象装饰者类。抽象装饰者类的主要功能是强制具体装饰者类实现对被装饰者类增加的功能。装饰者的类图如下:

装饰者模式实例
现在有一杯白开水(WaterClass),想向白开水里面加茶叶(TeaClass),加点糖(SugarClass),或者加点盐(SaltClass)。也许会认为那还不简单吗,加茶叶的白开水就再生成一个(WaterWithTeaClass),加糖的白开水就再生成一个(WaterWithSugarClass)这样不就行了吗?但是如果我们既要加糖又要加茶叶,或者加盐加糖这样的组合会很多,可能就要生成很多的类,造成类泛滥。这时候就要用到装饰者模式了。下面看看装饰者模式是如何轻松解决这个问题的。

WaterComponent:(被装饰者)

public class WaterComponent implements DrinkComponent{

    @Override
public void operation() {
// TODO Auto-generated method stub
System.out.print("water drink");
} }

SugarDecorator:(装饰者)

public class SugarDecorator extends DrinkDecorator{

    public SugarDecorator(DrinkComponent component) {
super(component);
// TODO Auto-generated constructor stub
}
public void operation()
{
component.operation();
System.out.print(",with sugar");
}
}

TeaDecorator:(装饰者)

public class TeaDecorator extends DrinkDecorator{

    public TeaDecorator(DrinkComponent component) {
super(component);
// TODO Auto-generated constructor stub
}
public void operation()
{
component.operation();
System.out.print(",with Tea");
}
}

MainClass

public class MainClass {

    public static void main(String[] args) {
// TODO Auto-generated method stub
WaterComponent water=new WaterComponent();
SugarDecorator sugar=new SugarDecorator(water);
//往白开水里面加糖。
TeaDecorator tea=new TeaDecorator(sugar);
//往加糖的白开水里面加茶叶。
tea.operation();
/*一杯有茶叶和糖的白开水就这样产生了,你也可以自由的往白开水里面加自己喜欢的东西,而并不造成类泛滥只需要继承DrinkDecorator就行*/
} }

由这个实例应该对装饰者模式有了了解,下面就来看看装饰者模式是如何应用到Java I/O中来的。

装饰者模式与Java I/O

先用一幅图来看看Java I/O到底是什么。下面的这幅图生动的刻画了Java I/O的作用。

由上图可知在Java中应用程序通过输入流(InputStream)的Read方法从源地址处读取字节,然后通过输出流(OutputStream)的Write方法将流写入到目的地址。流的来源主要有三种:

  • ①本地的文件(File)
  • ②控制台
  • ③通过socket实现的网络通信

下面的图可以看出Java中的装饰者类和被装饰者类以及它们之间的关系,这里只列出了InputStream中的关系。

由上图可以看出只要继承了FilterInputStream的类就是装饰者类,可以用于包装其他的流,装饰者类还可以对装饰者和类进行再包装。下面看一下Java中包装流的实例:

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class StreamDemo { public static void main(String[] args) throws IOException{ DataInputStream in=new DataInputStream(new BufferedInputStream(new FileInputStream("D:\\JAVAworkspace\\ProgramTest\\src\\StreamDemo.java")));
while(in.available()!=0)
{
System.out.print((char)in.readByte());
}
in.close();
} }

上面程序中对流进行了两次包装,先用 BufferedInputStream将FileInputStream包装成缓冲流也就是给FileInputStream增加缓冲功能,再DataInputStream进一步包装方便数据处理。

实现自己的包装流

了解了装饰者模式以及在Java中的应用,下面就来实现一个自己的包装流,要实现装饰者类,按照上面的图必须先得实现FilterInputStream装饰者抽象类。来实现这样一个操作的装饰者类:将输入流中的所有小写字母变成大写字母。

import java.io.FileInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream; public class UpperCaseInputStream extends FilterInputStream { protected UpperCaseInputStream(InputStream in) {
super(in);
} @Override
public int read() throws IOException {
int c=super.read(); return (c==-1?c:Character.toUpperCase(c));
} @Override
public int read(byte[] b, int off, int len) throws IOException {
int result=super.read(b, off, len);
for(int i=off;i<off+result;i++)
{
b[i]=(byte)Character.toUpperCase((char)b[i]);
}
return result;
} public static void main(String[] args) throws IOException {
int c;
InputStream in=new UpperCaseInputStream(new FileInputStream("D:\\JAVAworkspace\\ProgramTest\\src\\StreamDemo.java"));
try {
while((c=in.read())>=0)
{
System.out.print((char)c);
}
}
finally{
in.close();
}
} }

输出结果:

IMPORT JAVA.IO.BUFFEREDINPUTSTREAM;
IMPORT JAVA.IO.DATAINPUTSTREAM;
IMPORT JAVA.IO.FILEINPUTSTREAM;
IMPORT JAVA.IO.IOEXCEPTION;
PUBLIC CLASS STREAMDEMO { PUBLIC STATIC VOID MAIN(STRING[] ARGS) THROWS IOEXCEPTION{
DATAINPUTSTREAM IN=NEW DATAINPUTSTREAM(NEW BUFFEREDINPUTSTREAM(NEW FILEINPUTSTREAM("D:\\JAVAWORKSPACE\\PROGRAMTEST\\SRC\\STREAMDEMO.JAVA")));
WHILE(IN.AVAILABLE()!=0)
{
SYSTEM.OUT.PRINT((CHAR)IN.READBYTE());
}
IN.CLOSE();
} }

参考文献

1.java编程思想(Thinking In Java) 
2.Head First 设计模式 
3.https://www.javatpoint.com/java-io

原文:https://blog.csdn.net/u013309870/article/details/75735676

最新文章

  1. 3d旋转
  2. 【转】ASP.NET&quot;正在中止线程&quot;错误原因
  3. CSS包含块containing block详解
  4. &lt;context:annotation-config&gt; 和 &lt;context:component-scan&gt;的差别
  5. 【bzoj1006】[HNOI2008]神奇的国度
  6. CentOS中操作Psql
  7. Java入门到精通——基础篇之多线程实现简单的PV操作的进程同步
  8. Memcached 使用与简单监测
  9. JAVA G1收集器 第11节
  10. C# 数据库连接测试以及备份
  11. #Eureka 客户端和服务端间的交互
  12. javascript 函数的多义性
  13. JavaScript(简介)【Javascript历史】
  14. hbase高性能读取数据
  15. 0.react学习笔记-环境搭建与脚手架
  16. ionic3 验证比特币,以太坊,莱特币和其他流行的加密货币地址
  17. 洛谷P1360 [USACO07MAR]黄金阵容均衡题解
  18. HDFS 上文件块的副本数设置
  19. Unity3d中PureMVC框架的搭建及使用资料
  20. CF848C:Goodbye Souvenir(CDQ分治)

热门文章

  1. liunx系统问题总结
  2. POJ 2248 - Addition Chains - [迭代加深DFS]
  3. [No0000196]一文读懂Java 11的ZGC为何如此高效
  4. 在Windows Server 2008 R2 Server中,连接其他服务器的数据库遇到“未启用当前数据库的 SQL Server Service Broker,因此查询通知不受支持。如果希望使用通知,请为此数据库启用 Service Broker ”
  5. pat 1051Pop Sequence
  6. Cocos Creator cc.Event点击触摸事件详解
  7. 蓝桥杯 基础训练 2n皇后
  8. Select input 两个元素的宽度高度跟设定值不一致的问题
  9. 《数据仓库ETL工具箱》读书笔记
  10. 用PHP实现反向代理服务器