JavaIO之适配器模式

JavaIO之适配器模式

一、适配器模式

IO 流中的字符流和字节流的接口不同,它们之间可以协调工作就是基于适配器模式来做的,更准确点来说是对象适配器。通过适配器,我们可以将字节流对象适配成一个字符流对象,这样我们可以直接通过字节流对象来读取或者写入字符数据。

InputStreamReaderOutputStreamWriter 就是两个适配器(Adapter), 同时,它们两个也是字节流和字符流之间的桥梁。InputStreamReader 使用 StreamDecoder (流解码器)对字节进行解码,实现字节流到字符流的转换,OutputStreamWriter 使用StreamEncoder(流编码器)对字符进行编码,实现字符流到字节流的转换

InputStreamOutputStream 的子类是被适配者,InputStreamReaderOutputStreamWriter是适配器。

1
2
3
4
// InputStreamReader 是适配器,FileInputStream 是被适配的类(适配器模式)
InputStreamReader isr = new InputStreamReader(new FileInputStream(fileName), "UTF-8");
// BufferedReader 增强 InputStreamReader 的功能(装饰器模式)
BufferedReader bufferedReader = new BufferedReader(isr);

二、InputStreamReader类剖析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class InputStreamReader extends Reader {
// 用于解码的对象
private final StreamDecoder sd;
public InputStreamReader(InputStream in) {
super(in);
// 获取 StreamDecoder 对象
sd = StreamDecoder.forInputStreamReader(in, this,
Charset.defaultCharset()); // ## check lock object
}
public InputStreamReader(InputStream in, String charsetName)
throws UnsupportedEncodingException
{
super(in);
// 字符编码不能为空
if (charsetName == null)
throw new NullPointerException("charsetName");
// 获取 StreamDecoder 对象
sd = StreamDecoder.forInputStreamReader(in, this, charsetName);
}
// 使用 StreamDecoder 对象做具体的读取工作
public int read() throws IOException {
return sd.read();
}
public void close() throws IOException {
sd.close();
}
}

三、OutputStreamWriter类剖析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class OutputStreamWriter extends Writer {
// 用于编码的对象
private final StreamEncoder se;
public OutputStreamWriter(OutputStream out) {
super(out);
// 获取 StreamEncoder 对象
se = StreamEncoder.forOutputStreamWriter(out, this,
Charset.defaultCharset());
}
public OutputStreamWriter(OutputStream out, String charsetName)
throws UnsupportedEncodingException
{
super(out);
// 字符编码不能为空
if (charsetName == null)
throw new NullPointerException("charsetName");
// 获取 StreamEncoder 对象
se = StreamEncoder.forOutputStreamWriter(out, this, charsetName);
}
// 使用 StreamEncoder 对象做具体的写入工作
public void write(int c) throws IOException {
se.write(c);
}
}

适配器模式和装饰器模式有什么区别呢?

装饰器模式 更侧重于动态地增强原始类的功能,装饰器类需要跟原始类继承相同的抽象类或者实现相同的接口。并且,装饰器模式支持对原始类嵌套使用多个装饰器。

适配器模式 更侧重于让接口不兼容而不能交互的类可以一起工作,当我们调用适配器对应的方法时,适配器内部会调用适配者类或者和适配类相关的类的方法,这个过程透明的。就比如说 StreamDecoder (流解码器)和StreamEncoder(流编码器)就是分别基于 InputStreamOutputStream 来获取 FileChannel对象并调用对应的 read 方法和 write 方法进行字节数据的读取和写入。

1
2
3
4
5
StreamDecoder(InputStream in, Object lock, CharsetDecoder dec) {
// 省略大部分代码
// 根据 InputStream 对象获取 FileChannel 对象
ch = getChannel((FileInputStream)in);
}

适配器和适配者两者不需要继承相同的抽象类或者实现相同的接口。