[三]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其他类,字符编码 三十篇了,又是一个 ...
随机推荐
- SharePoint Column Format
https://docs.microsoft.com/en-us/sharepoint/dev/declarative-customization/column-formatting . { &quo ...
- Linux系统下zookeeper的安装和配置
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件.它是一个为分布式应用提供一致性服务的软件,提供的功 ...
- 181102 Python环境搭建(安装Sublime Text3)
利用Pycharm来编写.执行python代码是一个不错的选择,Pycharm的安装的确也很方便.但是偶然看到别人用Sublime Text来编写.执行代码,觉得很酷.所以自己动手搭建环境. 1. 下 ...
- Cmd命令 查看端口被占用
1)第一步 打开cmd命令窗口,输入命令:netstat -ano|findstr 输入端口号 2)第二步 继续输入命令:tasklist|findstr 第一步查询到的进程号 3)第三步 根据第二 ...
- django 利用pillow 进行简单的设置验证码(python)
1.导入模块 并定义一个验证状态 from PIL import Image, ImageDraw, ImageFont from django.utils.six import BytesIO de ...
- RabbitMQ消息队列系列教程(二)Windows下安装和部署RabbitMQ
摘要 本篇经验将和大家介绍Windows下安装和部署RabbitMQ消息队列服务器,希望对大家的工作和学习有所帮助! 目录 一.Erlang语言环境的搭建 二.RabbitMQ服务环境的搭建 三.Ra ...
- kali linux 网络渗透测试学习笔记(一)Nmap工具进行端口扫描
一.利用Nmap扫描网站服务器的端口开放情况 首先打开我们的kali linux才做系统,再打开其命令行模式,输入:nmap www.csdn.net 如下图所示: 因此,通过这个结果可以表明csdn ...
- 中间人攻击——ARP欺骗的原理、实战及防御
1.1 什么是网关 首先来简单解释一下什么是网关,网关工作在OSI七层模型中的传输层或者应用层,用于高层协议的不同网络之间的连接,简单地说,网关就好比是一个房间通向另一个房间的一扇门. 1.2 A ...
- 在Windows、Mac和 Linux系统中安装Python与 PyCharm
“工欲善其事,必先利其器”,本文介绍 Python环境的安装和 Python的集成开发环境(IDE) PyCharn的安装. 一.Python安装( Windows.Mac和 Linux) 当前主 ...
- [Swift]LeetCode565. 数组嵌套 | Array Nesting
A zero-indexed array A of length N contains all integers from 0 to N-1. Find and return the longest ...