文章目录

  • IO 类图
  • 看个例子
  • 装饰者模式
    • OutputStream家族类:
  • 清晰起来了
    • InputStream家族类:
    • Writer家族类:
    • Reader家族类:
    • 再看个例子
  • 最后的话

IO 类图

小帅最近在学Java的IO类库,这么多类看得小帅人头昏眼花,常常是学了这个类,忘了那个类,再过一阵子就全忘了。。。

每次用到的时候,小帅都要重新读文档,看代码,如此循环,身心疲惫。

小帅没办法只好向好朋友小会求助:IO类库太复杂了,我毫无头绪,能不能帮我梳理一下?

小会想了一下,说道:总体来说IO类库分为两大类:字节流字符流,字节流是按字节读取数据,字符流是按字符读取数据。

小帅不解:所有的数据在计算机中都是二进制表示的,都用字节来读取不就行了吗?为什么还要加个字符流,我用字节读出来,再转成字符不行吗?

小会说:Java中的字符都是用Unicode表示的,即对应一个数字,也就是码点。

如果我们用二进制的字节流读出来是无法看懂的,我们需要用对应的编码格式(比如:UTF-8,UFT-16等)转换成我们可读的字符。

字符流就是专门用来读写人们可读的字符的,这样会方便很多,一步到位,不用再手工转换成字符了。

我把IO类都放在一张图里,这样看上去就清爽了:

小帅:还是好多类啊。。。
小会:别急,我们往下看。

看个例子

我们看一下用字节流,把int数据1到9,写进txt文件的例子:

FileOutputStream outputStream = new FileOutputStream("text.txt");
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);for(int i = 0; i < 10; i++) {dataOutputStream.writeInt(i);}dataOutputStream.close();

text.txt文件内容:

里面保存的都是二进制数据,但是我们是用int的数据类型写进去的(dataOutputStream.writeInt(i);),而不是以二进制的格式写进去的。

小帅疑惑:为什么要用 DataOutputStream写入int数据呢?我直接用 FileOutputStream 不能写吗?DataOutputStream有什么作用呢?

小会微微一笑:如果不用DataOutputStream也可以,不过要自己拼成int数据类型的格式,一个int类型占四个字节,比如1用二进制表示就是 0000 0000 0000 0000 0000 0000 0000 0001,用十六进制表示就是 00 00 00 01。

我们试一下用 FileOutputStream 写入int数字 0,1,2:

 FileOutputStream outputStream = new FileOutputStream("text.txt");// int 0outputStream.write(0);outputStream.write(0);outputStream.write(0);outputStream.write(0);// int 1outputStream.write(0);outputStream.write(0);outputStream.write(0);outputStream.write(1);// int 2outputStream.write(0);outputStream.write(0);outputStream.write(0);outputStream.write(2);outputStream.close();

写入结果:

如果用DataOutputStream 写就是:dataOutputStream.writeInt(0),dataOutputStream.writeInt(1),dataOutputStream.writeInt(2),这样是不简单很多呢?

小帅似乎有点懂了:我知道了,DataOutputStream 是对 FileOutputStream 类功能的增强,让FileOutputStream 类更加强大,起到了装饰的作用。

小会开心道:你说到重点了,IO类看似凌乱,其实有一个精巧的设计模式,贯穿其中,把这么多类有序的组织起来了,这个设计模式是理解IO类的钥匙,你知道是哪一个设计模式吗?

装饰者模式?小帅疑惑道。

装饰者模式

是的,就是装饰者模式,我以前写过一篇介绍装饰者模式的文章,可以点开看看:装饰模式–小美的生日蛋糕。

装饰者模式的类图:

OutputStream家族类:

这里的FilterOutputStream类就是装饰模式中的抽象装饰类,它的子类BufferedOutputStream,DataOutputStream,PrintStream就是具体的装饰类,起到了功能增强的作用,它们本身并没有实现写数据的功能。

看下FilterOutputStream的代码:

写数据的功能是靠被修饰的类实现的,这里的OutputStream out 是要从外面传进来的:


DataOutputStream的writeInt方法实现了功能的增强,可以直接写int类型的数据:

BufferedOutputStream类实现了缓存的功能增强:

也就是说装饰类是给主类锦上添花,主类是锦,装饰类是花,花不能代替锦,主要的功能还得靠“锦”实现的。

清晰起来了

同理我们来看看其他流:

InputStream家族类:

Writer家族类:


小帅一眼看出了问题:奇怪,FilterWriter装饰类怎么没有子类呢? 是不是Writer家族没有用装饰模式呢?

小会微微一笑:不是的,其实还是用了装饰模式,只是实现的方式有点不一样,例如OutputStreamWriter类:


其实是对OutputStream类的装饰,换句话说字符流的底层其实是调用了字节流,这也很容易理解,因为计算机只能处理二进制数据,本质上还是通过字节流实现的。

Reader家族类:


FilterReader充当了抽象装饰类,PushbackReader是具体的装饰类。

同样的InputStreamReader类其实也实现了装饰模式:

再看个例子

public static void main(String[] args) throws IOException {try(BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("把酒问月·故人贾淳令予问之.txt")))) {writer.write("青天有月来几时?我今停杯一问之。");writer.newLine();writer.write("人攀明月不可得,月行却与人相随。");writer.newLine();writer.write("皎如飞镜临丹阙,绿烟灭尽清辉发。");writer.newLine();writer.write("但见宵从海上来,宁知晓向云间没。");writer.newLine();writer.write("白兔捣药秋复春,嫦娥孤栖与谁邻。");writer.newLine();writer.write("今人不见古时月,今月曾经照古人。");writer.newLine();writer.write("古人今人若流水,共看明月皆如此。");writer.newLine();writer.write("唯愿当歌对酒时,月光长照金樽里。");}try(BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("把酒问月·故人贾淳令予问之.txt")))) {String line;while((line = reader.readLine()) != null) {System.out.println(line);}}
}

输出:

青天有月来几时?我今停杯一问之。
人攀明月不可得,月行却与人相随。
皎如飞镜临丹阙,绿烟灭尽清辉发。
但见宵从海上来,宁知晓向云间没。
白兔捣药秋复春,嫦娥孤栖与谁邻。
今人不见古时月,今月曾经照古人。
古人今人若流水,共看明月皆如此。
唯愿当歌对酒时,月光长照金樽里。

OutputStreamWriter增强了FileOutputStream,让它拥有了直接写字符的能力,BufferedWriter增强了OutputStreamWriter,让它拥有了缓存的能力。

同样的,InputStreamReader增强了FileInputStream,让它拥有了直接读字符的能力,BufferedReader增强InputStreamReader,让它拥有了缓存的能力。

最后的话

Java的IO类库以前我也看得一脸懵逼,总是觉得太繁琐,太难记了。后来学了装饰者模式才知道,要搞懂Java的IO类库,其实重点是要搞懂装饰者模式。

如果不懂装饰者模式,看多少次也不会理解为什么要这么设计。

当一把锁被锁上的时候,你一直盯着锁看是没有用的,因为钥匙肯定不是插在锁上,一定要去别的地方找钥匙啊。

(欢迎你关注我的公众号:编程我也会)

戴上装饰者模式的眼镜,看透Java I/O相关推荐

  1. (十)装饰器模式详解(与IO不解的情缘)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处. LZ到目前已经写了九个设计模式,回过去看看,貌似写的有点凌乱,LZ后面会尽量改进. 那么本章LZ和各位读友讨论一个与JAVA中IO有着 ...

  2. 设计模式学习(三)——装饰器模式

    前言 距离上一次正儿八经地写随笔已经有一段时间了,虽然2月10号有一篇关于泛型的小记,但是其实只是简单地将自己的学习代码贴上来,为了方便后续使用时查阅,并没有多少文字和理解感悟.之所以在今天觉得有必要 ...

  3. java装饰者模式讲解视频教程_java装饰者模式介绍(图文教程)

    java装饰者模式介绍(图文教程).装饰者模式UML类图: 装饰者模式UML类图 java装饰者模式知识要点 装饰者模式动态地将责任附加到对象上.若要扩展功能,装饰者提供了比继承更有弹性的替代方案. ...

  4. 第 17 章 设计模式(3 装饰者模式)

    一.提出需求 咖啡馆订单系统项目(咖啡馆): 1. 咖啡种类/单品咖啡:Espresso(意大利浓咖啡).ShortBlack.LongBlack(美式咖啡).Decaf(无因咖啡): 2. 调料:M ...

  5. java中的装饰者模式[63]

    java中的装饰者模式[63] 文章目录 java中的装饰者模式[63] 一.什么是装饰者模式? 二.装饰者模式要点 三.代码演示 四.小结 一.什么是装饰者模式? **1.意图:**动态的给一个对象 ...

  6. 结构型设计模式(二) 之 装饰者模式可给对象一层层加工

    1 定义 装饰者模式(Decorator Pattern)又叫包装模式属于结构型设计模式之一,它主要用于动态地给一个对象增加一些额外扩展的功能,它是继承关系的一个替代方案,且在特定场景中比使用继承生成 ...

  7. Java/Android 设计模式系列(7)--装饰者模式

    这篇将会介绍装饰者模式(Decorator Pattern),装饰者模式也称为包装模式(Wrapper Pattern),结构型模式之一,其使用一种对客户端透明的方式来动态的扩展对象的功能,同时它也是 ...

  8. java/android 设计模式学习笔记(7)---装饰者模式

    这篇将会介绍装饰者模式(Decorator Pattern),装饰者模式也称为包装模式(Wrapper Pattern),结构型模式之一,其使用一种对客户端透明的方式来动态的扩展对象的功能,同时它也是 ...

  9. 如何利用装饰者模式在不改变原有对象的基础上扩展功能

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 作者:双子孤狼 blog.csdn.net/zwx9001 ...

最新文章

  1. 自动驾驶前沿报告!解密六大关键技术,全球人才分布
  2. UVA11100旅行(大包装小包,问最少多少个包)
  3. 互信息 卡方 - 文本挖掘
  4. oracle awr windows,windows下的oracle 如何生成awr报告
  5. 分区报无效的参数_西门子70系列变频器55KW上电就报F002故障维修
  6. TCP/IP 协议栈及 OSI 参考模型详解
  7. 高等数学下-赵立军-北京大学出版社-题解-练习12.3
  8. vscode 分支列表刷新_分钟将vscode撸成小霸王
  9. Android 系统(95)---Android build.prop参数详解
  10. node mysql 模块 封装_node.js基于工厂方法的mysql模块封装
  11. 01 前言/基础设施 - DevOps之路
  12. 全球传统证券外汇交易商进军区块链数字资产·统计(一)
  13. 俄罗斯方块游戏的算法
  14. 提高工作效率必备的生产力工具
  15. IOS开发—iOS视频拍摄与压缩
  16. 骨传导耳机工作原理,骨传导耳机优缺点
  17. 北航计算机组成实验课,北航计算机组成实验Project4
  18. 吉林大学计算机应用软件,吉林大学计算机科学与技术学院
  19. Java设计模式之工厂模式篇 (转)
  20. 一次kong概念验证之旅

热门文章

  1. xhtml与html5的关系,xhtml与html5的区别是什么?
  2. 如何快速启动ESXi 6.7
  3. deepin20系统安装VirtualBox
  4. 回家吃饭Android客户端整理后总结
  5. 产品经理常用的软件可推荐的有哪些?
  6. 入门智能车 | 带你认识PID闭环控制 - 增量式PID实现电机速度闭环
  7. 对《日本游记》的文学批评
  8. 省常中模拟 day2
  9. Hu矩---OpenCV-Python开发指南(26)
  10. vue3 点击按钮, 盒子向左移动