流与文件 <二> 流

1、简介

在面向字节的流中,可以从其中读入一个字节序列的对象称为输入流,向其中写入一个字节序列的对象称为输入流。字节序列的来源和目的地可以是文件,也可以是网络连接,甚至是内存块。
但面向字节的流不便于处理以字符形式(每个字符均由多个字节表示)存储信息,所以我们需要有面向字符的流。

Java IO

如上图所示,按照传输数据的数据格式的不同分为两种,分别为字符流、字节流。

简易版类图
简单版类图中仅展示了接口与继承关系,未对依赖关系进行描述。从类图中可以看出顶层接口有 Flushable(刷新数据的目标地,调用 flush 方法将所有已缓冲输出写入底层流。)、Closeable(关闭的数据源或目标,调用 close 方法可释放对象保存的资源。)、serializable(启用其序列化功能。序列化接口没有方法或字段,仅用于标识可序列化的语义。)。

2、字节流 : InputStream 、OutputStream

InputStream相关类图
InputStream相关类图如上图所示,其中主要包含有 FileInputStream(从文件系统中的某个文件中获得输入字节)、FilterInputStream(数据流的过滤)、ObjectInputStream(恢复序列化的对象)、PipedInputStream(管道输入流,多线程通过管道进行线程间通信)、SequenceInputStream、StringBufferInputStream、ByteArrayInputStream(包含一个内部缓冲区,缓冲区中有从流中读取的字节。)。

OutputStream相关类图
OutputStream相关类图如上图所示,其中包含有FileOutputStream、FilterOutputStream、ObjectOutputStream、ByteArrayOutputStream等

3、字符流 : Reader、Writer

Reader相关类图
Reader相关类图如上图所示,BufferedReader(从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。)、FilterReader(读取已过滤的字符流)、CharArrayReader(实现用作字符输入流的字符缓冲区)、PipedReader、InputStreamReader(字节转字符,使用指定的 charset 读取字节并将其解码为字符。)、StringReader。

Writer相关类图

Writer相关类图如上图所示,BufferedWriter(从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。)、FilterWriter(读取已过滤的字符流)、CharArrayWriter(实现用作字符输入流的字符缓冲区)、PipedWriter、OutputStreamWriter(字符转字节,使用指定的 charset 将要写入流中的字符编码成字节。)、StringReader。

4、字节字符转化:InputStreamReader、 OutputStreamWriter

4.1 转化过程

转化步骤
数据持久化或网络传输都是以字节进行的,所以必须要进行字符到字节或字节到字符的转化。以操作文件为例,内存中的字符数据需要通过OutputStreamWriter变为字节流才能保存在文件中,读取时需要将读入的字节流通过InputStreamReader变为字符流。

4.2 适配器模式

适配器的作用
如上图所示,适配器能够不改变原有代码,将现有系统的请求转换成厂商能够理解的请求。
适配器模式
如图所示,适配器对象将实现目标接口,并持有被适配对象。

我们拿火鸡对象来冒充缺少的鸭子对象时:TurkeyAdapter实现鸭子接口(以火鸡的行为替代鸭子的行为),持有火鸡对象。

1
2
3
4
5
6
package headfirst.adapter.ducks;

public interface Duck {
public void quack();
public void fly();
}

1
2
3
4
5
6
package headfirst.adapter.ducks;

public interface Turkey {
public void gobble();
public void fly();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package headfirst.adapter.ducks;

public class TurkeyAdapter implements Duck {
Turkey turkey;

public TurkeyAdapter(Turkey turkey) {
this.turkey = turkey;
}

public void quack() {
turkey.gobble();
}

public void fly() {
for(int i=0; i < 5; i++) {
turkey.fly();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
package headfirst.adapter.ducks;

public class WildTurkey implements Turkey {
public void gobble() {
System.out.println("Gobble gobble");
}

public void fly() {
System.out.println("I'm flying a short distance");
}
}

4.3 字符转字节 | 写入 | 编码 | OutputStreamWriter

OutputStreamWriter将使用选定的字符编码方式,将字符流转化为字节流
OutputStreamWriter类结构图
OutputStreamWriter实现了Writer接口,同时通过StreamEncoder间接持有了InputStream的引用,OutputStreamWriter使用OutputStream的行为来替代了Writer的行为。(当然没有那么简单,中间有涉及到StreamEncoder进行char到byte的编码)

StreamEncoder中流程间较多,可以调试查看。。。好吧,我承认我没看懂。。。

4.4 字节转字符 | 读取 | 解码 | InputStreamReader

InputStreamReader将使用选定的字符编码方式,将字节流转化为字符流
InputStreamReader类结构图
InputStreamReader实现了Reader接口,同时通过StreamDecoder间接持有了InputStream的引用,InputStreamReader使用InputStream的行为来替代了Reader的行为。(当然中间也涉及到StreamDecoder进行byte到char的解码)

参考文献:
[1] Java IO 详解
[2] JAVA字符串与字符编码处理的终极解决
[3] JAVA核心技术卷2
[4] 深入分析java web技术内幕
[5] Head First 设计模式
[6] Java_io体系之OutputStreamWriter、InputStreamReader简介、走进源码及示例——17