流分类回顾
本文是JavaIO告一段落的总结帖,希望对JavaIO做一个基础性的总结(不涉及NIO)。
从实现的角度进行简单的介绍,下面的这两个表格,之前出现过:
流分为输入输出流两种形式;数据格式又分为字节和字符两种形式;他们组合而来了四大家族;
InputStream;
OutputStream ;
Reader ;
Writer;
所有的四大家族的流有两种合成扩展方式:
按照数据源形式扩展;
按照装饰功能点扩展;
— 2 —
数据源形式扩展
现在我们换一个维度,从实现的角度,重新介绍下IO数据源扩展的根本从这种形式的数据中读取数据;
写入数据到这种数据形式;
我们上面列出来了ByteArray、File、Piped、Object、String/CharArray 这几种常用的数据源形式结合我们上面的概念,我们看一下,实际的实现
字节数组、字符数组、String
上面的这三种数据源形式,从上面看的话,逻辑非常清晰
有人可能觉得写到你自己内部存储里面干嘛,有毛用?
ByteArrayOutputStream
CharArrayWriter
StringWriter
内存数据,如果仅仅是存起来放到他自己肚子里面当然毛用没有但是,他们都提供了吐出来的功能了。给[字节数组 字符数组 String] 提供了一个统一的一致性的读写形式,操作非常方便,不是么
真实数据使用引用指向内部存储是内部的存储区
管道
pipe 管道用于直连,然后进行数据的传输,主要用于多线程数据共享
In 输入管道里面有一个存储区;
Out 输出管道内有个In的引用;
Connect之后,In指向了某个实际的 输入流,然后Out通过引用操作In里面的存储区,In自己的读方法也是操作这个存储区。
所以一旦理解了,JavaIO管道的模型,管道就实在是太简单了
只需要记住:
输入In里面 有一个存储缓冲区,输出有一个引用指向了In;
connect将他们连接起来,他们共同操作一个池子
输出往里面写,输入从里面读
管子的方向只能是 : 输出 -----> 输入
文件
文件相关的,都是实实在在的要通过操作系统了,所以也就必然需要使用本地方法
在Java中一个文件使用File来描述,File是抽象路径名,可以表示文件,也可以表示目录,File可以通过String路径名构造,另外还有文件描述符可以表示指代文件
底层文件本身是二进制存储的,如果你想要通过字符去操作文件,必然要经过 编码和解码的过程。
JavaIO提供了InputStreamReader,OutputStreamWriter两个转换流来实现编码和解码。
想要彻底理解还是需要理解适配器模式。
这两个流都是字符和字节的转换,只不过是方向不同;
从字节到字符,这就是解码 ;
从字符到字节,这就是编码;
InputStreamReader 字节流到字符流的桥梁, 也就是解码 从上图看,二进制才是码,从码到字符;OutputStreamWriter 字符流到字节流的桥梁, 也就是编码 从上图看,二进制才是码,从字符到码;
根据上面的说法,FileReader 和 FileWriter必然要是一种转换流
关于FileReader 和FileWriter说了那么多,其实只有两句话,就是字节流与字符流之间进行转换。
Reader reader = new InputStreamReader( new FileInputStream(.......));Writer writer = new OutputStreamWriter( new FileOutputStream(.......));
Object
对于文件中的字符与字节的转换,可以通过某些编码表,查表法确定编码的值,进而完成字符与字节之间的相互转换;那么,对于一个对象呢?Object是内存中的数据,他并不是一串字符形式,有一个概念叫做序列化与反序列化,其实就了类似字符的编码与解码。
从这个图应该能感知到ObjectInputStream和ObjectOutputStream与 字符流的逻辑类似了吧
字符与字节转换 是一种 编码解码的过程;
对象序列化与反序列化,不也是一种编码解码的过程吗?
只不过这个编码解码不是单纯的查询码表这么简单。
字符流与字节流的转换,可以通过转换流进行处理。
Object序列化与反序列化是:
ObjectInputStream与ObjectOutputStream他们分别实现ObjectInput 和 ObjectOutput来提供的;
所以从这个角度讲的话,可以把Object理解成为一种很特殊的"字符",他们两个就像InputStreamReader 和 OutputStreamWriter似的,用来转换 。
— 3 —
功能的装饰扩展
既然是功能的装饰扩展,我们之前已经说过很多次,都是装饰器模式,也就是说了很多遍的:
是你还有你,一切拜托你,中间增加点功能
这个你,就是需要被装饰的抽象角色Component
就是这四大家族:InputStream OutputStream Reader Writer
给读和写装饰增加新的功能,也就是最根本的读和写方法:将都是使用ConcreteComponent的
在基本的读和写方法之上,提供了新的功能。
缓冲的概念都是内部有一个缓冲区缓冲输入,是通过底层的流往自己的缓冲区写入数据,应用程序从缓冲输入的缓冲区中读取,提高了read速度缓冲输出,是把数据写入到自己的缓冲区中,后续再把数据通过底层的流一并写入,从而提高了write的速度;
因为读写都是从缓冲区中进行的了
LineNumberReader
内部使用了一个lineNumber = 0; 用来记录行号,这个行号可以使用方法设置和获取。
getLineNumber setLineNumber 但是他不改变流的位置。
PushBack
装饰器模式 方法依赖于被装饰的实体 ConcreteComponent只是内部有一个缓冲区,可以存放被回退掉的字符,所有的读取方法在进行读取的时候,都会查看缓冲区的数据。
提供了多种形式的打印,根本只是在真的写入数据前,将数据参数进行一些处理。
根本的写操作,依赖被装饰的节点流提供,在数据写入之前进行必要的数据处理
所以你看,扩展的功能通过装饰器模式,他们的行为都是类似的,那就是:1. 最基本的读写依赖被装饰的具体的节点流2. 然后进行了功能的增强
— 4 —
总结
说到这个地方,我们又从实现的角度把常用的一些流进行了介绍,你会发现看起来那么多,实际上并没有多少。四大家族,以及几种数据源形式,以及几个扩展功能点。只要找准了思路,理清楚了逻辑,就不难理解了。
不知道到底是恍然大悟?还是?恍然如梦呢?
https://mp.weixin.qq.com/s/EBcuFw7ipGapm_VXLQ-xHg