[二十六]JavaIO之再回首恍然(如梦? 大悟?)



流分类回顾


本文是JavaIO告一段落的总结帖
但愿对JavaIO作一个基础性的总结(不涉及NIO)
从实现的角度进行简单的介绍

下面的这两个表格,以前出现过
数据源形式 InputStream OutputStream Reader Writer
ByteArray(字节数组) ByteArrayInputStream  ByteArrayOutputStream 
File(文件) FileInputStream  FileOutputStream  FileReader FileWriter 
Piped(管道) PipedInputStream  PipedOutputStream  PipedReader  PipedWriter 
Object(对象) ObjectInputStream  ObjectOutputStream 
String StringBufferInputStream  StringReader  StringWriter 
CharArray(字符数组) CharArrayReader  CharArrayWriter 

扩展功能点 InputStream OutputStream Reader Writer
Data(基本类型) DataInputStream   DataOutputStream 
Buffered(缓冲) BufferedInputStream  BufferedOutputStream  BufferedReader  BufferedWriter   
LineNumber(行号) LineNumberInputStream LineNumberReader 
Pushback(回退) PushbackInputStream  PushbackReader 
Print(打印) PrintStream  PrintWriter


流分为输入输出流两种形式
数据格式又分为字节和字符两种形式
他们组合而来了四你们族
InputStream        OutputStream        Reader        Writer
全部的四你们族的流有两种合成扩展方式:
按照数据源形式扩展
按照装饰功能点扩展



数据源形式扩展


如今咱们换一个维度,从实现的角度,从新介绍下IO

数据源扩展的根本
从这种形式的数据中读取数据
写入数据到这种数据形式

咱们上面列出来了ByteArray  File   Piped    Object  String  CharArray 这几种经常使用的数据源形式
结合咱们上面的概念,咱们看一下,实际的实现

字节数组 / 字符数组 /String

ByteArray
内存数据
ByteArrayInputStream 内部有一个byte buf[] 引用
指向实际保存数据的那个字节数组
ByteArrayInputStream(byte buf[])
ByteArrayInputStream(byte buf[], int offset, int length)
构造方法将内部的byte buf[] 引用指向某个 byte buf[]
而后就从这里读
ByteArrayOutputStream 内部有一个byte buf[]缓冲区 构造方法初始化这个缓冲区,也就是分配空间
数据的写,就是写到这里面


CharArray
内存数据
CharArrayReader 内部有一个 char buf[]; 引用
指向实际保存数据的那个字符数组
CharArrayReader(char buf[])
public CharArrayReader(char buf[], int offset, int length)
构造方法将内部的char buf[]; 引用指向某个char buf[]
而后就从这里读
CharArrayWriter 内部有一个char buf[] 缓冲区
构造方法初始化这个缓冲区,也就是分配空间
数据的写,就是写到这里面



String
内存数据
StringReader 内部有一个 String str;引用
指向实际的那个提供数据的String
StringReader(String s)
构造方法将内部的str   引用指向某个String
而后就从这里读
StringWriter 内部有一个StringBuffer buf 用于保存数据
public StringWriter()
StringWriter(int initialSize)
构造方法初始化这个StringBuffer,就是new StringBuffer时能够指定大小 
数据的写,就是写到这里面  也就是StringBuffer.append

上面的这三种数据源形式,从上面看的话,逻辑很是清晰
读--->从哪里读?--->你给我一个数据源--->我以IO的操做习惯(InputStream/Reader) 读给你
写--->IO的操做习惯写(OutputStream/Writer) --->写到哪里?--->写到我本身内部的存储里

有人可能以为写到你本身内部存储里面干吗,有毛用?

ByteArrayOutputStream image_5b9b6870_54fc
CharArrayWriter image_5b9b6870_1ff1
StringWriter image_5b9b6870_5824

内存数据,若是仅仅是存起来放到他本身肚子里面固然毛用没有
可是,他们都提供了吐出来的功能了
给[字节数组 字符数组  String] 提供了一个统一的一致性的读写形式,操做很是方便,不是么

image_5b9b6870_7808
真实数据使用引用指向
内部存储是内部的存储区



管道

pipe 管道用于直连 而后进行数据的传输
主要用于多线程数据共享

In 输入管道里面有一个存储区
Out 输出管道内有个In的引用

Connect以后,In指向了某个实际的 输入流

而后Out经过引用操做In里面的存储区
In本身的读方法也是操做这个存储区
image_5b9b6870_f2f


Pipe
PipedInputStream 内部有存储区byte buffer[]  PipedInputStream()
PipedInputStream(int pipeSize)
构造方法中给存储区分配空间,能够指定存储区的大小,不然默认值
PipedOutputStream 内部有一个PipedInputStream sink 引用 PipedOutputStream()
PipedOutputStream(PipedInputStream snk)
无参的后续还须要调用connect
有参数的建立对象时就进行connect链接
PipedReader 内部有存储区 char buffer[];   PipedReader()
PipedReader(int pipeSize)
构造方法中给存储区分配空间,能够指定大小,不然默认值
PipedWriter 内部有一个PipedReader sink;  引用 PipedWriter()
PipedWriter(PipedReader snk)
无参的后续还须要调用connect
有参数的建立对象时进行connect链接


因此一旦理解了,JavaIO管道的模型,管道就实在是太简单了
image_5b9b6870_4d4f
只须要记住:
输入In里面 有一个存储缓冲区, 输出有一个引用指向了In
connect将他们链接起来,他们共同操做一个池子
输出往里面写,输入从里面读
管子的方向只能是 :    输出 -----> 输入





文件
文件相关的,都是实实在在的要经过操做系统了
因此也就必然须要使用本地方法

在Java中一个文件使用File来描述,File是抽象路径名 能够表示文件  也能够表示目录
File能够经过String路径名构造
另外还有文件描述符能够表示指代文件


File
磁盘数据
FileInputStream 操做文件
构造方法可使用:  File /String的路径名 /文件描述符   来建立
实实在在的一个InputStream的实现类,最终经过本地方法来进行数据读取
FileOutputStream 操做文件
构造方法可使用: File/ String的路径名 /文件描述符     来建立
另外他还有是否追加的概念
实实在在的一个OutputStream的实现类,最终经过本地方法来进行数据写入


底层文件自己是二进制存储的,若是你想要经过字符去操做文件,必然要通过 编码和解码的过程
image_5b9b6870_6d14
JavaIO提供了InputStreamReader    OutputStreamWriter两个转换流来实现编码和解码
想要完全理解仍是须要理解适配器模式
这两个流都是字符和字节的转换,只不过是方向不一样
从字节到字符,这就是解码  ;   从字符到字节,这就是编码
InputStreamReader   字节流到字符流的桥梁, 也就是解码   从上图看,二进制才是码,从码到字符
OutputStreamWriter 字符流到字节流的桥梁, 也就是编码   从上图看,二进制才是码,从字符到码


根据上面的说法,FileReader 和 FileWriter必然要是一种转换流

File
磁盘数据
FileReader
文件自己都是二进制序列 字节形式,因此必然有字节InputStream到字符Reader的转换
而InputStreamReader 偏偏是字节通向字符的桥梁
因此 FileReader继承了InputStreamReader  是一种特殊的InputStreamReader

InputStreamReader 将InputStream适配成Reader   因此须要InputStream做为参数进行构造
文件的字节输入流--FileInputStream可使用:  File /String的路径名 /文件描述符  来建立
因此FileReader的构造方法接受这几种参数, 构造一个FileInputStream
而后调用InputStreamReader 的构造方法

InputStreamReader字节到字符 解码 涉及到码表  InputStreamReader构造方法部分须要指定编码
FileWriter
文件自己都是二进制序列 字节形式,因此想要写入数据必然有字符Writer到字节OutputStream的转换
而OutputStreamWriter 偏偏是字符通向字节的桥梁
因此 FileWriter继承了OutputStreamWriter  是一种特殊的OutputStreamWriter

OutputStreamWriter 将OutputStream适配成Writer   因此须要OutputStream做为参数进行构造
文件的字节输出流 --FileOutputStream可使用:  File /String的路径名 /文件描述符   来建立
因此FileWriter的构造方法接受这几种参数
而后构造一个FileOutputStream,调用OutputStreamWriter 的构造方法

另外FileOutputStream 还有追加的概念
因此FileWriter 的构造方法里面也有append 追加这一项

OutputStreamWriter字符到字节 编码 涉及到编码 OutputStreamWriter构造方法部分须要指定编码

关于FileReader 和FileWriter说了那么多
其实只有两句话,就是字节流与字符流之间进行转换
Reader reader = new InputStreamReader( new FileInputStream(.......));
Writer writer = new OutputStreamWriter( new FileOutputStream(.......));



Object
对于文件中的字符与字节的转换,能够经过某些编码表,查表法肯定编码的值,进而完成字符与字节之间的相互转换
那么,对于一个对象呢?
Object是内存中的数据,他并非一串字符形式
有一个概念叫作         序列化与反序列化
其实就了相似  字符的编码与解码
image_5b9b6870_3444
从这个图应该能感知到ObjectInputStream和ObjectOutputStream    与 字符流的逻辑相似么
字符与字节转换 是一种  编码解码的过程
对象序列化与反序列化 不也是一种编码解码的过程吗 ,只不过这个编码解码不是单纯的查询码表这么简单
字符流与字节流的转换,能够经过转换流进行处理
Object序列化与反序列化是
ObjectInputStream  ObjectOutputStream  他们分别实现ObjectInput 和 ObjectOutput来提供的
因此从这个角度讲的话,能够把Object理解成为一种很特殊的"字符"
他们两个就像InputStreamReader  和 OutputStreamWriter似的,用来转换
 

功能的装饰扩展

既然是功能的装饰扩展,咱们以前已经说过不少次,都是装饰器模式
也就是说了不少遍的
是你还有你,一切拜托你,中间增长点功能
这个,就是须要被装饰的抽象角色Component   
就是这四你们族      InputStream        OutputStream        Reader        Writer   
给读和写装饰增长新的功能,也就是最根本的读和写方法,将都是使用ConcreteComponent
在基本的读和写方法之上,提供了新的功能

Data
DataInputStream 继承自FilterInputStream
获得一个InputStream引用in

构造方法须要InputStream
经过in.read系列方法读取,   而后将读取的数据    组装成基本数据类型
进而提供读取基本数据类型的能力
DataOutputStream 继承自FilterOutputStream
获得一个OutputStream 引用out

构造方法须要OutputStream
将基本类型数据进行转化处理,    而后调用out.write系列方法将数据写入
进而提供写入基本数据类型的能力


缓冲的概念都是内部有一个缓冲区
缓冲输入  是经过底层的流往本身的缓冲区写入数据, 应用程序从缓冲输入的缓冲区中读取,提升了read速度
缓冲输出  是把数据写入到本身的缓冲区中,后续再把数据经过底层的流一并写入,从而提升了write的速度
由于读写都是从缓冲区中进行的了
Buffered
BufferedInputStream
继承自FilterInputStream
获得一个InputStream引用in
构造方法须要InputStream

内部有一个缓冲区byte buf[]
BufferedOutputStream
继承自FilterOutputStream
获得一个OutputStream 引用out
构造方法须要OutputStream

内部有一个缓冲区buf[];
BufferedReader 内部有Reader 引用 in
构造方法须要一个Reader

内部有一个缓冲区char cb[];
BufferedWriter 内部有一个Writer 引用  out
构造方法须要一个Writer

内部有一个缓冲区char cb[];

LineNumberReader
内部使用了一个lineNumber = 0;  用来记录行号
这个行号可使用方法设置和获取
getLineNumber  setLineNumber  可是他不改变流的位置

PushBack
装饰器模式 方法依赖于被装饰的实体 ConcreteComponent
只是内部有一个缓冲区,能够存放被回退掉的字符
全部的读取方法在进行读取的时候,都会查看缓冲区的数据
PushbackInputStream

继承自FilterInputStream 获得一个InputStream 引用in
构造方法须要 InputStream

内部有缓冲区byte[] buf
FilterReader 继承自FilterReader 获得一个Reader引用 in
构造方法须要一个Reader

内部有缓冲区char[] buf


Print
提供了多种形式的打印,根本只是在真的写入数据前,将数据参数进行一些处理
根本的写操做 依赖被装饰的节点流提供
在数据写入以前进行必要的数据处理
PrintStream 继承自 FilterOutputStream获得一个OutputStream 引用 out
构造须要一个OutputStream
PrintWriter 内部有一个out
构造方法须要一个Writer


因此你看,扩展的功能经过装饰器模式,他们的行为都是相似的,那就是:
1. 最基本的读写依赖被装饰的具体的节点流
2. 而后进行了功能的加强


总结

说到这个地方,咱们又从实现的角度把经常使用的一些流进行了介绍
你会发现看起来那么多,实际上并无多少
四你们族,以及几种数据源形式,以及几个扩展功能点
只要找准了思路,理清楚了逻辑,就不难理解了

不知道究竟是恍然大悟?仍是?恍然如梦 ?
相关文章
相关标签/搜索