[三]JavaIO之IO体系类整体设计思路 流的概念以及四大基础分类
1.流
流的字面含义

| 物质 一个地方 流向了 另一个地方 |

| 主体 | 人 人从一个地方到了另一个地方 |
| 源/目的 | 学校/家 |
| 方向 | 回家或者返校 图中的两个箭头 |
| 中间形式 | 火车和汽车 |
1.1流到底是什么
|
源/主体内容/方向
水桶, 作为一个容器,他就是 源
里面可能装了水,也可能装了酒,还可能装了别的, 这就是数据形式主体
箭头表示方向,到底是往里面流还是往外抽
|
| 中间形式 我们都知道,水管可不全都是一种规格的,就拿家里装修常用的水管就有 4/6分管 两种 如果有一个阀门或者水龙头是6分管,而你接入桶里面的是4分管 怎么办? 显然,你会找一个转接头一类的东西,这就是下图中的黄色,橙红色,绿色代表的部分,他们就是不同的水管 通过转接头与另外一种规格的水管能够连接起来,这就是中间形式
|
| 抽水模型中的流 在这个例子中,流就是对于抽水这一过程的抽象描述. 一个水管里面的水的走向, 设定好了之后, 就是固定了的, 他要么往这头, 要么往另一头 而且,他肯定有一个源头/目的,水管得水不能凭空来,水管也不能凭空抽取水 另外,水管显然不仅仅只是能抽取水, 他还可以输送别的液体,比如酒,水,饮料,酱油醋...... 而且,如果有了各种不同规格的水管以及转接头,你的这个管道将会更加强大 所以说,容器 桶 接上了水管,就有能力提供水或者存储水,他就是一个流 |
1.2程序语言中的流的主要概念
| 含义/源/方向/数据形式/中间形式 |
|
流的含义:
在程序设计中,流是对于数据流动传输的一种抽象描述
任何有能力产出数据的数据源,或者有能力接受数据的接收端对象都是一个流 (也就是上面例子中的一个容器接上水管)
|
|
流的源和目的:
数据可能从本地文件读取,或者写入, 也可能发送到网络上,这就是源和目的
|
|
流的方向:
同水管里面的流水一样,也是只有两个方向,流进或者流出,也就是我们常说的输入 和 输出
|
| 流的数据形式: 数据的具体形式就是流传送的内容,可能是字节,也能是字符,这就是数据的形式 |
| 流的中间形式: 对于任何一个流对它的功能进行一些必要的扩充,就好像接上了转接头的流可以接到其他规格的水管一样 在一个流的基础上 包装,装饰上其他的一些功能,流就会变得更加强大 |
1.3 流相关概念详细解读
1.3.1 流的源和目的
|
1.文件
最基本的一个数据源就是我们前文提到过的文件,文件不仅java中有,其他语言中也拥有文件的概念
|
|
2.字节数组
数据最基本的单位是字节
数组是在程序设计中,为了处理方便, 把具有相同类型的若干变量按有序的形式组织起来的一种形式
这些按序排列的同类数据元素的集合称为数组
所以字节数组,自然是为了更方便操作字节的一种数据组织形式
|
|
3. 字符数组/String对象
既然数组可以简化更方便的进行操作,而且也有字节数组
是不是还应该有字符数组呢?
而且,java中的String对象 ,它的内部实现也是char数组,java中使用char表示字符,这不就是字符数组么
|
|
4. 管道
"管道"的概念也是类似字面含义,一端输入,就可以从一端流出,就好像一个水管一样,
主要用来多线程之间直接进行数据交互,所以说数据来源也可能是一个管道
|
|
5.网络等
其他数据源比如网络等,java的强项就是WEB,从网络接收数据是再自然不过的事情
|
| 6.流 另外流本身也可以作为一种源,所以一个流的源可以来自另外的一个流 |
1.3.2 流的方向
|
流的方向很简单,只有两个方向,输入 或者 输出
|
1.3.3 流的数据形式
|
计算机存储数据是二进制的 0 1 序列
计算机中存储容量的最小的单位是位(bit)最基本的单位是字节(byte)
字节是通过网络传输信息(或在硬盘或内存中存储信息)的单位
也就是说任何其他形式的数据,都可以而且,最终也都是用字节来表示
所以数据最基本的形式就是字节
1 byte = 8 bit
|
|
我们的世界充满了各种符号
字符是表示数据和信息的字母、数字或其他符号
在电子计算机中,每一个字符与一个二进制编码相对应,这是一个编码的过程
|
| 所以说,数据的基本形式有 字节 和 字符两种形式 |
1.3.4流的中间形式
| 放学回家的例子,我们很清楚的知道,火车和汽车是我们 人的中间形式过程,经过转换(买票上车), 地上的人看不到我们了,看到的只是火车 对于流来说,中间形式是什么样子的呢? 比如我们想要把一个Int类型直接写入到文件中,怎么办呢? 我们是不是需要把这个类型的数据处理下 转换下呢 或者说包装下 就如同你坐上了车(车把你装了进去,形式就是车),总之就是要处理下 比如想要缓冲,按照行,按照字等等 这就是一种中间形式,后面我们会详细介绍涉及到的中间形式 |
| 不过很显然,中间形式并没有向从某种数据源读取数据那么刚需 但是他会给你提供更多的功能,让你的流功能更加多变,扩展 如果有了中间形式,你可能就能够直接把一个int写入到文件上,这不是很方便么 |
1.3.5流的种类-基本功能 扩展功能
| 想要完成一个IO类库的基本功能,只需要把握住三点 |
|
1. 流的源和目的
2. 流的数据形式
3. 流的方向
|
| 想要做得更好就需要把握好流的中间形式,提供更强大的功能 |
| 流的源和目的 | 文件 / 字节数组 /管道 /字符数组/String对象 / 网络 / 流 |
| 流的数据形式 | 字符 / 字节 |
| 流的方向 | 输入 / 输出 |
| 文件(源) | 输入 | 字节 |
| 文件(源) | 输入 | 字符 |
| 文件(目的地) | 输出 | 字节 |
| 文件(目的地) | 输出 | 字符 |
| 字节数组(源) | 输入 | 字节 |
| 字节数组(源) | 输入 | 字符 |
| 字节数组(目的地) | 输出 | 字节 |
| 字节数组(目的地) | 输出 | 字符 |
| 管道(源) | 输入 | 字节 |
| 管道(源) | 输入 | 字符 |
| 管道(目的地) | 输出 | 字节 |
| 管道(目的地) | 输出 | 字符 |
2. JAVA IO类库体系结构
2.1 流的四大家族
| 输入 | 字节 |
| 输出 | 字节 |
| 输入 | 字符 |
| 输出 | 字符 |
| 四种形式 | 输入字节 | 输出字节 | 输入字符 | 输出字符 |
| Java中名称 | InputStream | OutPutStream | Reader | Writer |
|
可以看得出来在命名上,类库设计者的一些想法
把字节使用Stream作为后缀,或许因为字节是最基本的单位,所以他才是流Stream
我们平时阅读 read和书写write的都是字符,所以使用Reader 和 Writer表示字符的输入和输出也很自然
|
节点流与过滤流
2.2.1 InputStream
2.2.1.1 InputStream节点流
| 字节数组 | ByteArrayInputStream (java.io) |
| 文件 |
FileInputStream (java.io)
|
| 管道 |
PipedInputStream (java.io)
|
| String |
|
| 对象 | ObjectInputStream (java.io) |
| 类名 | 功能 | 构造方法 |
| 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 对象读取 |
PipedInputStream(PipedOutputStream src) PipedInputStream(PipedOutputStream src, int pipeSize) PipedInputStream() PipedInputStream(int pipeSize) |
| 弃用,如果条件允许可以考虑使用StringReader | ||
| ObjectInputStream |
对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化
ObjectOutputStream 和 ObjectInputStream 分别与 FileOutputStream 和 FileInputStream 一起使用时
可以为应用程序提供对对象图形的持久存储
ObjectInputStream 用于恢复那些以前序列化的对象
其他用途包括使用套接字流在主机之间传递对象,或者用于编组和解组远程通信系统中的实参和形参。
|
ObjectInputStream(InputStream in) ObjectInputStream() |
| SequenceInputStream (java.io) | 两个或者多个InputStream对象转换为单一的InputStream | SequenceInputStream(InputStream s1, InputStream s2) SequenceInputStream(Enumeration<? extends InputStream> e) |

2.2.1.2 InputStream过滤流
|
我们之前就提到过,希望能够有直接操作数据类型的流,通过这个流可以直接操作基本数据类型的读写,而不需要自己去处理字节或者字节数组等
也就是说我们希望能够对基本数据类型进行支持
|
|
IO是操作系统的瓶颈,如果过于频繁的直接对磁盘IO进行读写,势必会增加CPU的空闲,性能降低,我们希望能够有缓冲的功能
|
| IDE开发工具的编辑器都有行号的标志,行号可以给我们提供很多的便捷性,所以希望能够跟踪展示行号 |
| 比如当我们用程序读取一行代码,识别其中的关键字 比如 int i = 0; 读取到int时,我们不知道他是不是关键字,可能是一个int0的变量名 读取到下一个的时候,发现是空格,我们才能确定,他就是一个关键字 但是下面的空格已经被读取了,我们可能希望接下来的扫描能够读取到空格,可是流是顺序的,被消费了就不存在了 所以希望能够把读取到的字节回退到原来的流中 |
|
DataInputStream (java.io)
BufferedInputStream (java.io)
LineNumberInputStream (java.io)
PushbackInputStream (java.io)
|
| 看下类图,黑色部分为装饰器模式的角色 节点流表示上面说到的节点流 ByteArrayInputStream/FileInputStream/PipedInputStream/ObjectInputStream/ FilterInputStream中包含一个InputStream属性(是你还有你) ![]() |
|
|
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)
|
2.2.2 OutputStream
2.2.2.1 OutputStream节点流
| 字节数组 | ByteArrayOutputStream (java.io) |
| 文件 |
FileOutputStream (java.io)
|
| 管道 |
PipedOutputStream (java.io)
|
| 对象 | ObjectOutputStream (java.io) |
| 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() |
2.2.2.2 OutputStream过滤流
| 基本数据类型支持/缓冲/便捷输出 |
|
DataOutputStream (java.io)
BufferedOutputStream (java.io)
PrintStream (java.io)
|
| 非IO包中的,但是却跟IO相关的一些功能点,跟OutputStream相关的类 |
|
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)
|
2.2.3 Reader
2.2.3.1 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 | 转换为Reader InputStreamReader 是字节流通向字符流的桥梁 它使用指定的 charset 读取字节并将其解码为字符 它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集 每次调用 InputStreamReader 中的一个 read() 方法都会导致从底层输入流读取一个或多个字节 为了达到最高效率,可要考虑在 BufferedReader 内包装 InputStreamReader |
InputStreamReader(InputStream in) InputStreamReader(InputStream in, String charsetName) InputStreamReader(InputStream in, Charset cs) InputStreamReader(InputStream in, CharsetDecoder dec) 构造方法很清晰,接受一个InputStream 并且可以自定义字符编码 |
|
属于适配器模式中的对象适配器模式
Reader 是Target
InputStream 是 被适配者 Adaptee
InputStreamReader 是 Adapter
|
2.2.3.2 Reader过滤流
|
BufferedReader (java.io)
LineNumberReader (java.io)
PushbackReader (java.io)
|
|
在BufferedReader中,可以理解为只有一个具体的装饰器的简化版本
省略了抽象类
直接继承自Reader
BufferedReader 融合了Decoder 和 ConcreteDecoder两者
是你还有你 BufferedReader 是Reader 还有一个Reader
|
| 在FilterReader中就跟之前的字节流中的装饰器模式的应用基本一致了 FilterReader 表示抽象的装饰器部件 Decoder PushbackReader 表示具体的装饰器 ![]() 是你还有你 FilterReader 是 Reader 还有 Reader |
2.2.4 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 作为转换器使用 OutputStreamWriter 是字符流通向字节流的桥梁 可使用指定的 charset 将要写入流中的字符编码成字节 使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集 每次调用 write() 方法都会导致在给定字符(或字符集)上调用编码转换器 为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中
例如:
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) 获取OutputStream然后进行转换,或者指定具体的字符编码 |
| FilterWriter | 类似其他的Filter类,作为装饰器模式的Decoder角色 以便具体的装饰器角色可以使用 |
2.3 IO类层次结构总结
| IO的逻辑功能设计点 由 数据源,流的方向,流的数据形式三部分组合而成,这个组合构成了IO的基本功能 另外还有扩展功能,扩展功能以基础功能作为依托,底层依赖基本功能 每种形式的基本功能和扩展功能构成了该形式的功能的集合 |
| 数据源形式比较多,但是对于流的数据形式以及流的方向是固定的 所以所有的类的基础,都是基于 流的数据形式以及流的方向的组合 也就是 字节输入 字节输出 字符输入 字符输出 这四个形式是固定的 分别使用 InputStream OutputStream Reader Writer来表示这四大家族 前面两个表示字节 后面两个表示字符 |
| 绝大多数的扩展都以 上面四个名词作为后缀,表示是他的家族成员 |
| 基本功能对于字节涉及下面几个关键词 ByteArray File Piped Object 扩展功能对于字节涉及涉及下面几个关键词
Data Buffered Pushback LineNumber print
|
|
基本功能对于字符涉及涉及下面几个关键词
CharArray String File Piped
扩展功能对于字符涉及涉及下面几个关键词 |
| 虽然四大家族都由基本功能以及扩展功能组成 但是字符和字节的实现形式却并不完全相同 字节流的扩展功能比较依赖装饰器角色FilterInputStream 以及 FilterOutputStream 但是字符流的扩展功能不完全依赖FilterReader 以及 FilterWriter |
| 数据源与四大家族的结合组合成了基本功能 也就是节点流 扩展功能点与四大家族的结合组成了扩展功能 也就是过滤流 |
| 另外还有几个工具一样的存在 SequenceInputStream 用于合并InputStream InputStreamReader 以及OutputStreamWriter 用于转换 使用了适配器模式 |
[三]JavaIO之IO体系类整体设计思路 流的概念以及四大基础分类的更多相关文章
- legend2---开发日志1(legend的数据库整体设计思路是什么)
legend2---开发日志1(legend的数据库整体设计思路是什么) 一.总结 一句话总结:不同种类的物品分不同的表放,不放到一个物品表里,取所有物品时一个个表的取就好了 不同种类的物品分不同的表 ...
- MASA Framework - 整体设计思路
源起 年初我们在找一款框架,希望它有如下几个特点: 学习成本低 只需要学.Net每年主推的技术栈和业务特性必须支持的中间件,给开发同学减负,只需要专注业务就好 个人见解:一款好用的框架应该是补充,而不 ...
- Java - IO System类支持和缓冲流
System类的支持和缓冲流 System类对IO的支持 在System类中,为了支持IO操作提供了三个常量: 错误输出: public static final PrintStream err; 输 ...
- JAVA基础知识之IO——Java IO体系及常用类
Java IO体系 个人觉得可以用"字节流操作类和字符流操作类组成了Java IO体系"来高度概括Java IO体系. 借用几张网络图片来说明(图片来自 http://blog.c ...
- Alink漫谈(十二) :在线学习算法FTRL 之 整体设计
Alink漫谈(十二) :在线学习算法FTRL 之 整体设计 目录 Alink漫谈(十二) :在线学习算法FTRL 之 整体设计 0x00 摘要 0x01概念 1.1 逻辑回归 1.1.1 推导过程 ...
- ibatis源码学习1_整体设计和核心流程
背景介绍ibatis实现之前,先来看一段jdbc代码: Class.forName("com.mysql.jdbc.Driver"); String url = "jdb ...
- 高效IO之Java IO体系(一)
更多Android高级架构进阶视频学习请点击:https://space.bilibili.com/474380680 个人觉得可以用“字节流操作类和字符流操作类组成了Java IO体系”来高度概括J ...
- Java IO体系之File类浅析
Java IO体系之File类浅析 一.File类介绍 位于java.io下的Java File类以抽象的方式代表文件名和目录路径名.该类主要用于文件和目录的创建.文件的查找和文件的删除等.File对 ...
- JAVA之旅(三十)——打印流PrintWriter,合并流,切割文件并且合并,对象的序列化Serializable,管道流,RandomAccessFile,IO其他类,字符编码
JAVA之旅(三十)--打印流PrintWriter,合并流,切割文件并且合并,对象的序列化Serializable,管道流,RandomAccessFile,IO其他类,字符编码 三十篇了,又是一个 ...
随机推荐
- matlab安装 macos
http://pan.baidu.com/s/1o6qKdxo内附安装说明Matlab R2014A Mac & Linux 破解版 readme文件有流程!可以安装
- ssm知识点整理
第1章 resultType和resultMap的区别是什么? MyBatis中在查询进行select映射的时候,返回类型可以用resultType,也可以用resultMap,resultType ...
- Katalon Studio之swagger中的API导入
约束条件: swagger中一定要在注解@ApiOperation中设置nickname的唯一值,例如: @ApiOperation(value="新增用户",notes=&quo ...
- Springboot 拦截器 依赖注入失败
解决方案2种. ====1 https://blog.csdn.net/shunhua19881987/article/details/78084679 ====2 https://www.cnblo ...
- node koa2 玩起来都是中间件啊
玩的我想吐 !!! 整理下常用的中间件吧! 先列在这有空把这些中间件的使用技巧也写出来分享一下koa-router 路由中间件koa-bodyparser POST数据处理的中间件koa-stri ...
- JS Fetch
使用Fetch 1.进行 fetch 请求 一个基本的 fetch请求设置起来很简单.看看下面的代码: fetch('http://example.com/movies.json') .then(fu ...
- Unity进阶----AssetBundle_03(2018/11/07)
1. 为啥有AB包? 因为资源需要更新, 避免更新一次打包一次 动态修改. 2. AB包注意啥? 依赖关系 找依赖关系应该找到对应的平台!!! 3. 打包策略是分场景打包 若文件被文件夹包含打包出来的 ...
- 算法与数据结构(八) AOV网的关键路径(Swift版)
上篇博客我们介绍了AOV网的拓扑序列,请参考<数据结构(七) AOV网的拓扑排序(Swift面向对象版)>.拓扑序列中包括项目的每个结点,沿着拓扑序列将项目进行下去是肯定可以将项目完成的, ...
- Hive如何处理小文件问题?
一.小文件是如何产生的 1.动态分区插入数据,产生大量的小文件,从而导致map数量剧增. 2.reduce数量越多,小文件也越多(reduce的个数和输出文件是对应的). 3.数据源本身就包含大量的小 ...
- Java堆和栈的区别和介绍,JVM的堆和栈
一.Java的堆内存和栈内存 Java把内存划分成两种:一种是堆内存,一种是栈内存. 堆:主要用于存储实例化的对象,数组.由JVM动态分配内存空间.一个JVM只有一个堆内存,线程是可以共享数据的. ...


