JavaIO之IO体系类整体设计思路 流的概念以及基础分类(四)
从本文开始,将正式进入JavaIO的简介在继续javaIO系列的文章之前
可以过去看一下 本人博客上的设计模式中的 适配器模式和装饰器模式
这会对接下来的阅读大有帮助
本文是从逻辑上介绍整个的Java IO类家谱画像.
### **流**
计算机以及互联网的世界发展不过短短几十年,但是这几十年却是日新月异
系统的复杂度也越来越高,程序设计语言的抽象程度也越来越高
但是无论如何都绕不开一个话题,那就是IO
之前已经介绍过,IO 就是输入 和 输出,入和出是相对于应用程序来说的
而且,经常一个程序的输出可能是另一个程序的输入
这本身就是一个抽象的概念
并没有"必须怎么样,那才叫IO"的说法
从数据库,从文件,从内存,从网络拿数据,你都可以叫做输入,数据写出,都可以叫做输出,这并没有什么好纠结的
在java中使用流这一概念来描述输入和输出
#### 流的字面含义
!(data/attachment/forum/202208/15/161245hujd1rdu881jjkw8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
百度百科中是这样描述流的,可以看得出来,流本身就包含了 这样一层含义
> 物质 一个地方 流向了 另一个地方
在继续之前,我们先回想下放暑假或者开学时候的场景
假定你需要做汽车和火车,如下图所示
!(data/attachment/forum/202208/15/161327piz5nnfzwyo5ob98.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
上图中有几个关键概念
| 概念 | 逻辑 |
| -------- | --------------------------------- |
| 主体 | 人 人从一个地方到了另一个地方 |
| 源/目的| 学校/家 |
| 方向 | 回家或者返校 图中的两个箭头 |
| 中间形式 | 火车和汽车 |
### **流到底是什么**
我们再举一个比较简单的例子, 使用 水管 往桶里面加水或者抽水
**源/主体内容/方向**
水桶, 作为一个容器,他就是源
里面可能装了水,也可能装了酒,还可能装了别的,这就是数据形式主体
箭头表示方向,到底是往里面流还是往外抽
!(data/attachment/forum/202208/15/161507i1p2py1vjypjazz1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
#### 中间形式
我们都知道,水管可不全都是一种规格的,就拿家里装修常用的水管就有 4/6分管 两种
如果有一个阀门或者水龙头是6分管,而你接入桶里面的是4分管
怎么办?
显然,你会找一个转接头一类的东西
这就是下图中的黄色,橙红色,绿色代表的部分,他们就是不同的水管
通过转接头与另外一种规格的水管能够连接起来,这就是中间形式
!(data/attachment/forum/202208/15/161603t3v23f44m688ljwm.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
#### 抽水模型中的流
在这个例子中,流就是对于抽水这一过程的抽象描述.
一个水管里面的水的走向, 设定好了之后, 就是固定了的, 他要么往这头, 要么往另一头
而且,他肯定有一个源头/目的,水管得水不能凭空来,水管也不能凭空抽取水
另外,水管显然不仅仅只是能抽取水, 他还可以输送别的液体,比如酒,水,饮料,酱油醋......
而且,如果有了各种不同规格的水管以及转接头,你的这个管道将会更加强大
所以说,容器 桶 接上了水管,就有能力提供水或者存储水,他就是一个流
如果这个流在整合上各种不同规格的转接头和水管,能力将会大大提升
### **程序语言中的流的主要概念**
| **含义/源/方向/数据形式/中间形式** |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 流的含义:在程序设计中,流是对于数据流动传输的一种抽象描述任何有能力产出数据的数据源,或者有能力接受数据的接收端对象都是一个流 (也就是上面例子中的一个容器接上水管) |
| 流的源和目的:数据可能从本地文件读取,或者写入,也可能发送到网络上,这就是源和目的 |
| 流的方向:同水管里面的流水一样,也是只有两个方向,流进或者流出,也就是我们常说的输入和输出 |
| 流的数据形式:数据的具体形式就是流传送的内容,可能是字节,也能是字符,这就是数据的形式 |
| 流的中间形式:``对于任何一个流对它的功能进行一些必要的扩充,就好像接上了转接头的流可以接到其他规格的水管一样``在一个流的基础上 包装、装饰上其他的一些功能,流就会变得更加强大 |
### **流相关概念详细解读**
#### **流的源和目的**
| 流的源和目的 |
| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **1.文件**最基本的一个数据源就是我们前文提到过的文件,文件不仅java中有,其他语言中也拥有文件的概念 |
| **2.字节数组**数据最基本的单位是字节数组是在程序设计中,为了处理方便, 把具有相同类型的若干变量按有序的形式组织起来的一种形式这些按序排列的同类数据元素的集合称为数组所以字节数组,自然是为了更方便操作字节的一种数据组织形式 |
| **3. 字符数组/String对象**既然数组可以简化更方便的进行操作,而且也有字节数组是不是还应该有字符数组呢?而且,java中的String对象,它的内部实现也是char数组,java中使用char表示字符,这不就是字符数组么 |
| **4. 管道**"管道"的概念也是类似字面含义,一端输入,就可以从一端流出,就好像一个水管一样,主要用来多线程之间直接进行数据交互,所以说数据来源也可能是一个管道 |
| **5.网络等**其他数据源比如网络等,java的强项就是WEB,从网络接收数据是再自然不过的事情 |
| **6.流**另外流本身也可以作为一种源,所以一个流的源可以来自另外的一个流 |
#### **流的方向**
流的方向很简单,只有两个方向,输入 或者 输出
#### **流的数据形式**
| 数据形式 |
| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 计算机存储数据是二进制的 0 1 序列计算机中存储容量的最小的单位是位(bit)最基本的单位是**字节** (byte)字节是通过网络传输信息(或在硬盘或内存中存储信息)的单位也就是说任何其他形式的数据,,都可以而且最终也都是用字节来表示所以数据最基本的形式就是字节 1byte = 8 bit |
| 我们的世界充满了各种符号字符是表示数据和信息的字母、数字或其他符号在电子计算机中,每一个字符与一个二进制编码相对应,这是一个编码的过程 |
> 数据的基本形式有字节 和 字符两种形式
#### 流的中间形式
放学回家的例子,我们很清楚的知道,火车和汽车是我们 人的中间形式过程,经过转换(买票上车),地上的人看不到我们了,看到的只是火车
对于流来说,中间形式是什么样子的呢?
比如我们想要把一个Int类型直接写入到文件中,怎么办呢?
我们是不是需要把这个类型的数据处理下 转换下呢或者说包装下 就如同你坐上了车(车把你装了进去,形式就是车),总之就是要处理下
比如想要缓冲,按照行,按照字等等
这就是一种中间形式,后面我们会详细介绍涉及到的中间形式
不过很显然,中间形式并没有向从某种数据源读取数据那么刚需
但是他会给你提供更多的功能,让你的流功能更加多变,扩展
如果有了中间形式,你可能就能够直接把一个int写入到文件上,这不是很方便么
#### **流的种类**
| 基本功能 扩展功能 |
| ------------------------------------------------------------------------------------ |
| 想要完成一个IO类库的基本功能,只需要把握住三点1.流的源和目的2.流的数据形式3.流的方向 |
| 想要做得更好就需要把握好流的中间形式,提供更强大的功能 |
| 种类 | 属性 |
| ------------ | ------------------------------------------------------ |
| 流的源和目的 | 文件 / 字节数组 /管道 /字符数组/String对象 / 网络 / 流 |
| 流的数据形式 | 字符/字节 |
| 流的方向 | 输入/ 输出 |
现在我们掌握了流的基本属性,上表中的三种,也掌握了他们可能的变量值
很简单,只需要使用简单的组合进行计算,我们就可以列举出来所有可能的组合
下面我们试着列一些(并不会列出来全部内容)
| 源、目的 | 方向 | 形式 |
| ---------------- | ---- | ---- |
| 文件(源) | 输入 | 字节 |
| 文件(源) | 输入 | 字符 |
| 文件(目的地) | 输出 | 字节 |
| 文件(目的地) | 输出 | 字符 |
| 字节数组(源) | 输入 | 字节 |
| 字节数组(源) | 输入 | 字符 |
| 字节数组(目的地) | 输出 | 字节 |
| 字节数组(目的地) | 输出 | 字符 |
| 管道(源) | 输入 | 字节 |
| 管道(源) | 输入 | 字符 |
| 管道(目的地) | 输出 | 字节 |
| 管道(目的地) | 输出 | 字符 |
等等................等等................
还有很多种组合,我相信你肯定可以排列的出来
不过很显然,我们此处只是简单的罗列,穷举出所有组合的可能
对于类库的设计自然不能这么简单暴力,或许有些组合没有必要,或许有些组合不符合逻辑
去掉那些无用的,不合逻辑的,无意义的,那么剩下来的组合形式,其实就是IO类库要解决的问题
也就是就剩下了我们现在看得到的JavaIO类库了 接下来从整体上对IO类库进行介绍
### **JAVAIO类库体系结构**
**java.io**
!(data/attachment/forum/202208/15/162554gsj6cc6b8jf69s6u.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
java.io包中(JDK8),有87个类,其中有一些辅助类 还有一些异常类
去掉这些之后,剩下的绝大多数都是IO类体系的直接相关类,看起来很杂乱繁多
我们接下来讲从整体上对涉及到的IO类进行介绍,等看完本篇文章,相信你应该能有一个整体的把控
只有从整体把控才有可能掌握整个完整的类家族
### **流的四大家族**
如果先不考虑数据的来源,根据流的方向(输入 和 输出)以及流的数据形式(字符 和 字节) 我们有四种形式
| 方向 | 形式 |
| ---- | ---- |
| 输入 | 字节 |
| 输出 | 字节 |
| 输入 | 字符 |
| 输出 | 字符 |
| 四种形式 | 输入字节 | **输出字节** | **输入字符** | **输出字符** |
| ---------- | ----------- | ------------------ | ------------------ | ------------------ |
| Java中名称 | InputStream | OutPutStream | Reader | Writer |
可以看得出来在命名上,类库设计者的一些想法
把字节使用Stream作为后缀,或许因为字节是最基本的单位,所以他才是流Stream
我们平时阅读read和书写write的都是字符,所以使用Reader 和 Writer表示字符的输入和输出也很自然
!(data/attachment/forum/202208/15/162737xlaw7v43youuzo15.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
### 节点流与过滤流
我们上面讲述流的含义概念时,反复提到了流的基本功能以及中间形式
基本功能就是针对于不同数据源的操作,属于刚需范围
而中间形式则是刚需的强有力的增强
流的数据源/目的流的方向流的数据类型的组合,构成了基本功能的完整集合
而对于增强型的流的形式,则是Java IO出彩的地方,属于增强型的功能
java中针对于基本数据源进行操作的流叫做 **节点流**
而对于那些起到增强装饰功能的流,叫做**过滤流**
按照我们上面的思维逻辑
只需要把相关的数据源与我们上面的这四种基本形式进行组合
就可以得到流的基本功能家族,也就是节点流
根据节点流需要的拓展功能,我们就可以推演出来过滤流
### 流体系类层次结构详解
#### InputStream节点流
**数据源与InputStream的结合**
| 源/目的| 组合 |
| -------- | ------------------------------------ |
| 字节数组 | ByteArrayInputStream (java.io) |
| 文件 | FileInputStream (java.io) |
| 管道 | PipedInputStream (java.io) |
| String | ~StringBufferInputStream (java.io)~ |
| 对象 | ObjectInputStream (java.io) |
上面就是IO类库提供给我们的基础功能
也就是可用的有效的合理的**数据源与InputStream的组合**(InputStream流的方向与流的数据形式的组合)
| 类名 | 功能 | 构造方法 |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| ByteArrayInputStream | 从字节数组中读取数据,也就是从内存中读取数据包含一个内部缓冲区,指向该字节数组内部计数器跟踪 read 方法要提供的下一个字节关闭 ByteArrayInputStream 无效此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException | ByteArrayInputStream(byte buf[])ByteArrayInputStream(byte buf[], int offset, int length)不是复制而来,直接指向地址``多参数的带偏移量 |
| FileInputStream | 用于从文件中读取信息 | FileInputStream(String name)FileInputStream(File file) FileInputStream(FileDescriptor fdObj)使用文件路径名抽象路径名File 或者文件描述符 |
| PipedInputStream | 产生用于写入相关Pipe的OutputStream的数据实现管道化的概念 管道输入流应该连接到管道输出流; 管道输入流提供要写入管道输出流的所有数据字节 通常,数据由某个线程从 PipedInputStream 对象读取并由其他线程将其写入到相应的 PipedOutputStream 不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程 | PipedInputStream(PipedOutputStream src) PipedInputStream() PipedInputStream(int pipeSize) |
| ~StringBufferInputStream~ | 弃用,如果条件允许可以考虑使用StringReader | |
| ObjectInputStream | 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化ObjectOutputStream 和 ObjectInputStream 分别与 FileOutputStream 和 FileInputStream 一起使用时可以为应用程序提供对对象图形的持久存储ObjectInputStream 用于恢复那些以前序列化的对象其他用途包括使用套接字流在主机之间传递对象,或者用于编组和解组远程通信系统中的实参和形参。 | ObjectInputStream(InputStream in) ObjectInputStream() |
SequenceInputStream可以说既不是节点流也不是过滤流,硬要算的话,可以说是节点流
算是一个工具类一样的存在
| 类名 | 功能 | 构造方法 |
| ---------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------- |
| equenceInputStream (java.io) | 两个或者多个InputStream对象转换为单一的InputStream | SequenceInputStream(InputStream s1, InputStream s2)SequenceInputStream(Enumeration e) |
!(data/attachment/forum/202208/15/163959sy20f3nhr2031f1y.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
#### InputStream过滤流
介绍过了InputStream的节点流
我们看下,我们还希望InputStream能够哪些扩展的功能,也就是上面提到过的 流的中间形式
我们之前就提到过,希望能够有直接操作数据类型的流,通过这个流可以直接操作基本数据类型的读写,而不需要自己去处理字节或者字节数组等
也就是说我们希望能够对基本数据类型进行支持
IO是操作系统的瓶颈,如果过于频繁的直接对磁盘IO进行读写,势必会增加CPU的空闲,性能降低,我们希望能够有**缓冲**的功能
IDE开发工具的编辑器都有行号的标志,行号可以给我们提供很多的便捷性,所以希望能够跟踪展示行号
比如当我们用程序读取一行代码,识别其中的关键字
比如 int i = 0; 读取到int时,我们不知道他是不是关键字,可能是一个int0的变量名
读取到下一个的时候,发现是空格,我们才能确定,他就是一个关键字
但是下面的空格已经被读取了,我们可能希望接下来的扫描能够读取到空格,可是流是顺序的,被消费了就不存在了
所以希望能够把读取到的字节**回退**到原来的流中
于是就有了
支持基本数据类型/缓存/行号/回退这几种扩展功能的想法
功能点和InputStream组合下可以得到如下四种扩展功能
Data表示基本数据类型Buffer 表示缓冲LineNumber表示行号 PushBack表示回退
```java
DataInputStream (java.io)
BufferedInputStream (java.io)
LineNumberInputStream (java.io)
PushbackInputStream (java.io)
```
!(data/attachment/forum/202208/15/164347m2gaiankvu29hkfk.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
到底怎么实现呢?
显然我们可以直接通过实现InputStream来实现这几个子类,用于表示这几个功能
但是就又出现了一个问题,如果既想要 支持基本数据类型,又想具有缓冲的功能怎么办? 如果还用继承的想法会出现什么问题?
那就又回到了组合的问题上来了,4种功能就会出现4*3*2*1=24 中组合,类的个数直接爆炸了.....
回想下我们之前想到过的设计模式---> 装饰器模式
就可以完美的解决这个问题,装饰器模式是继承的一种良好替代方式,能过有效的避免类的个数的爆炸问题
并且还能够动态的增加或者减少功能
看下UML图
!(data/attachment/forum/202208/15/164405gqpmtq06ootriq2p.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
通过UML图可以看得到,我们还需要一个Decorator类,我们的具体的装饰类个数不止一个,显然不能省略这个Decorator抽象类
(不清楚装饰器模式的没办法理解这种逻辑,请务必看明白装饰器模式)
这个Decorator就是我们的FilterInputStream (java.io)
看下类图,黑色部分为装饰器模式的角色
节点流表示上面说到的节点流
ByteArrayInputStream/FileInputStream/PipedInputStream/ObjectInputStream/~StringBufferInputStream~
FilterInputStream中包含一个InputStream属性(是你还有你)
!(data/attachment/forum/202208/15/164449euqqqvh6iq8vbyfv.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
!(data/attachment/forum/202208/15/164500rwtntqwstttsqatq.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
下面我们看下InputStream下的类继承体系
现在你是否已经可以大致的明白,这些类都是做什么的了呢?
!(data/attachment/forum/202208/15/164528o33r869lpb31zylz.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
另外还有一些不在java.io包中的类
这些不是IO主体系内的东西,但是依赖于IO ,从事着跟IO相关的一些工作,所以也扩展自InputStream
后面或许会单独进行介绍,此处不展开讨论
```java
SocketInputStream (java.net)
CheckedInputStream (java.util.zip)
DeflaterInputStream (java.util.zip)
GZIPInputStream (java.util.zip)
InflaterInputStream (java.util.zip)
ZipInputStream (java.util.zip)
JarInputStream (java.util.jar)
```
!(data/attachment/forum/202208/15/164619f8fx0m22dxmsd4wl.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
### OutputStream
#### OutputStream节点流
**数据源与OutputStream的结合**
| 源/目的| 组合 |
| -------- | ------------------------------- |
| 字节数组 | ByteArrayOutputStream (java.io) |
| 文件 | FileOutputStream (java.io) |
| 管道 | PipedOutputStream (java.io) |
| 对象 | ObjectOutputStream (java.io) |
仍旧是数据源与OutputStream的组合
| 类名 | 功能 | 构造方法 |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| ByteArrayOutputStream | 其中的数据被写入一个 byte 数组缓冲区会随着数据的不断写入而自动增长, 可使用 toByteArray() 和 toString() 获取数据 关闭 ByteArrayOutputStream 无效此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException | ByteArrayOutputStream() ByteArrayOutputStream(int size) 无参会调用有参,设置默认值 |
| FileOutputStream | 信息写入文件 | FileOutputStream(String name) FileOutputStream(String name, boolean append) FileOutputStream(File file) FileOutputStream(File file, boolean append) FileOutputStream(FileDescriptor fdObj) 与FileInputStream几乎一样,不同的是第二个参数用于设置是否是append追加 |
| PipedOutputStream | 可以将管道输出流连接到管道输入流来创建通信管道管道输出流是管道的发送端 通常,数据由某个线程写入 PipedOutputStream 对象并由其他线程从连接的 PipedInputStream 读取 不建议对这两个对象尝试使用单个线程,因为这样可能会造成该线程死锁 | PipedOutputStream(PipedInputStream snk) PipedOutputStream() |
| ObjectOutputStream | ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream可以使用 ObjectInputStream 读取(重构)对象 通过在流中使用文件可以实现对象的持久存储 如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象 | ObjectOutputStream(OutputStream out) ObjectOutputStream() |
!(data/attachment/forum/202208/15/165027tonn9kzow0kodxkk.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
#### **OutputStream过滤流**
类似InputStream,OutputStream也需要有支撑基本数据类型的功能,以及缓冲的功能
另外,既然是输出,还希望能够输出各种类型的数据,这样子将会更加方便
也就是
**基本数据类型支持/缓冲/便捷输出**
```java
DataOutputStream (java.io)
BufferedOutputStream (java.io)
PrintStream (java.io)
```
!(data/attachment/forum/202208/15/165120rhzv3rvrebvvdnbw.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
同InputStream 一样
扩展的功能,类库设计者依然是使用装饰器模式
FilterOutputStream (java.io)是我们的Decorator
本文作者:程序员潇然 疯狂的字节X https://crazybytex.com/
!(data/attachment/forum/202208/15/165138skydxqq2m1xyz4m2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
完整的家谱
!(data/attachment/forum/202208/15/165158am77z639b1qpqq3m.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
非IO包中的,但是却跟IO相关的一些功能点,跟OutputStream相关的类
```java
SocketOutputStream (java.net)
CheckedOutputStream (java.util.zip)
DeflaterOutputStream (java.util.zip)
GZIPOutputStream (java.util.zip)
InflaterOutputStream (java.util.zip)
JarOutputStream (java.util.jar)
ZipOutputStream (java.util.zip)
```
扩展的家谱
!(data/attachment/forum/202208/15/165243m8wapalle7lzaa44.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
### **Reader**
#### **Reader节点流**
**数据源与Reader的结合**
| 源/目的| 组合形式 |
| -------- | ------------------------- |
| 字符数组 | CharArrayReader (java.io) |
| String | StringReader (java.io) |
| 文件 | FileReader (java.io) |
| 管道 | PipedReader (java.io) |
| 类名 | 功能 | 构造方法 |
| --------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| CharArrayReader | 实现一个可用作字符输入流的字符缓冲区 | CharArrayReader(char buf[])CharArrayReader(char buf[], int offset, int length) |
| StringReader | 其源为一个字符串的字符流 | StringReader(String s) |
| FileReader | 用来读取字符文件的便捷类 | FileReader(String fileName)FileReader(File file) FileReader(FileDescriptor fd) |
| PipedReader | 管道字符输入流 | PipedReader(PipedWriter src)PipedReader(PipedWriter src, int pipeSize) PipedReader()PipedReader(int pipeSize) |
字节和字符作为数据的存储单位,自然经常有转换的需要
InputStreamReader 就是InputStream 转换为Reader的类
| 类名 | 功能 | 构造方法 |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| utStreamReader | 转换为ReaderInputStreamReader 是字节流通向字符流的桥梁它使用指定的 charset 读取字节并将其解码为字符它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集 每次调用 InputStreamReader 中的一个 read() 方法都会导致从底层输入流读取一个或多个字节 为了达到最高效率,可要考虑在 BufferedReader 内包装 InputStreamReader | InputStreamReader(InputStream in) InputStreamReader(InputStream in, String charsetName)InputStreamReader(InputStream in, Charset cs) InputStreamReader(InputStream in, CharsetDecoder dec) 构造方法很清晰,接受一个InputStream 并且可以自定义字符编码 |
对于类的转换,设计模式中使用了适配器模式
通过构造方法接收InputStream,然后通过内部的StreamDecoder处理
StreamDecoder和 StreamEncoder是作为字符输入和输出转换的关键类,后续有时间会介绍到
!(data/attachment/forum/202208/15/165743jqaj0qcdlfzlfqdk.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
!(data/attachment/forum/202208/15/165750kqejkwsbq1xcw444.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
属于适配器模式中的对象适配器模式
Reader 是Target
InputStream 是 被适配者 Adaptee
InputStreamReader 是Adapter
需要注意的是FileReader 与字节流中的FileInputStream 和 FileOutputStream 也是不一样的
FileReader 继承 InputStreamReader
#### **Reader过滤流**
字符流Reader也依然有装饰器模式的应用
```java
BufferedReader (java.io)
LineNumberReader (java.io)
PushbackReader (java.io)
```
!(data/attachment/forum/202208/15/165826ojprnpzvdp6bvll3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
不过需要注意,Reader字符流的装饰器模式应用跟字节流的有些差别
在字节流中,扩展功能都是通过FilterInputStream 或者 FilterOutputStream
然而,在我们的Reader中
BufferedReader和 FilterReader 各自是一个装饰器模式
在BufferedReader中,可以理解为只有一个具体的装饰器的简化版本
省略了抽象类
直接继承自Reader
BufferedReader融合了Decoder 和 ConcreteDecoder两者
!(data/attachment/forum/202208/15/165939jrasyariz0h5i5wo.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
是你还有你 BufferedReader 是Reader 还有一个Reader
在FilterReader中就跟之前的字节流中的装饰器模式的应用基本一致了
FilterReader 表示抽象的装饰器部件Decoder
PushbackReader 表示具体的装饰器
!(data/attachment/forum/202208/15/170002jq68xx66qyxsegqy.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
是你还有你FilterReader 是 Reader 还有 Reader
Reader家族完整的族谱
!(data/attachment/forum/202208/15/170037r1b46me0lfmed5z4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
### **Writer**
**数据源与writer的结合**
| 字符数组 | CharArrayWriter (java.io) |
| -------- | ------------------------- |
| String | StringWriter (java.io) |
| 文件 | FileWriter (java.io) |
| 管道 | PipedWriter (java.io) |
| 类名 | 功能 | 构造方法 |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| CharArrayWriter | 实现一个可用作 Writer 的字符缓冲区缓冲区会随向流中写入数据而自动增长可使用 toCharArray() 和 toString() 获取数据。在此类上调用 close() 无效并且在关闭该流后可以调用此类中的各个方法,而不会产生任何 IOException | CharArrayWriter() CharArrayWriter(int initialSize) 内部包含char buf[] size为大小 构造方法用来初始化缓冲区 |
| StringWriter | 将输出收集到一个字符缓冲区 StringBuffer的字符流,可以用来构造字符串关闭 StringWriter 无效 此类中的方法在关闭该流后仍可被调用,而不会产生任何 IOException | StringWriter() StringWriter(int initialSize) 构造方法初始化缓冲区 |
| FileWriter | 用来写入字符文件的便捷类类似FileReader继承自InputStreamReader 他继承自OutputStreamWriter | FileWriter(String fileName) FileWriter(String fileName, boolean append) FileWriter(File file) FileWriter(File file, boolean append) FileWriter(FileDescriptor fd) 构造方法都是用来设置文件 |
| PipedWriter | 管道字符流 | PipedWriter(PipedReader snk) PipedWriter() |
#### 转换流
| 类名 | 功能 | 构造方法 |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| OutputStreamWriter | 类似InputStreamReader <br />作为转换器使用OutputStreamWriter 是字符流通向字节流的桥梁 <br />可使用指定的 charset 将要写入流中的字符编码成字节<br />使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集 <br /><br />每次调用 write() 方法都会导致在给定字符(或字符集)上调用编码转换器 <br />为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中<br />例如:Writer out = new BufferedWriter(new OutputStreamWriter(System.out)); | OutputStreamWriter(OutputStream out, String charsetName) OutputStreamWriter(OutputStream out) OutputStreamWriter(OutputStream out, Charset cs) OutputStreamWriter(OutputStream out, CharsetEncoder enc) <br />获取OutputStream然后进行转换,或者指定具体的字符编码 |
| 类名 | 功能 | |
| ------------ | -------------------------------------------------------------------------------------- | - |
| FilterWriter | 类似其他的Filter类<br />作为装饰器模式的Decoder角色 <br />以便具体的装饰器角色可以使用 | |
BufferedWriter 以及PrintWriter类似Reader不同于字节流的装饰器模式应用
他们都自成一个模式的应用
他俩都单独是Writer 也都包含一个Writer
!(data/attachment/forum/202208/15/170647yxab88ffcdvnrarb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
Writer下完整的家谱
!(data/attachment/forum/202208/15/170700suuu0fsv5es0uscr.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
### **IO类层次结构总结**
前面已经对IO类的基本层次结构进行了一个逻辑上的概述
我们现在归纳概括下一些基本特点
**IO的逻辑功能设计点 由 数据源,流的方向,流的数据形式三部分组合而成,这个组合构成了IO的基本功能**
另外还有扩展功能,扩展功能以基础功能作为依托,底层依赖基本功能
每种形式的基本功能和扩展功能构成了该形式的功能的集合
数据源形式比较多,但是对于流的数据形式以及流的方向是固定的
所以所有的类的基础,都是基于流的数据形式以及流的方向的组合
也就是 字节输入 字节输出 字符输入字符输出
这四个形式是固定的
分别使用 InputStreamOutputStreamReaderWriter来表示这四大家族
前面两个表示字节后面两个表示字符
绝大多数的扩展都以 上面四个名词作为后缀,表示是他的家族成员
基本功能对于字节涉及下面几个关键词
ByteArray FilePipedObject
扩展功能对于字节涉及涉及下面几个关键词
DataBufferedPushbackLineNumber print
基本功能对于字符涉及涉及下面几个关键词
CharArrayString FilePiped
扩展功能对于字符涉及涉及下面几个关键词
Buffered Print
虽然四大家族都由基本功能以及扩展功能组成
但是字符和字节的实现形式却并不完全相同
字节流的扩展功能比较依赖装饰器角色FilterInputStream以及 FilterOutputStream
但是字符流的扩展功能不完全依赖FilterReader以及FilterWriter
数据源与四大家族的结合组合成了基本功能也就是节点流
扩展功能点与四大家族的结合组成了扩展功能 也就是过滤流
另外还有几个工具一样的存在
SequenceInputStream 用于合并InputStream
InputStreamReader 以及OutputStreamWriter 用于转换 使用了适配器模式
本文主要是从逻辑上介绍了IO家族,虽然实现上都略有差异
但是基本的命名习惯和功能点四个家族是非常类似的
只有理解了类库的逻辑出发点,才能理解IO整个的类库,而本文正是从逻辑上去解读类库的设计
!(data/attachment/forum/202206/16/141330jha7st9soow8772i.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "common_log.png")
`转载务必注明出处:程序员潇然,疯狂的字节X,https://crazybytex.com/thread-136-1-1.html `
页:
[1]