12.1.9 Java中的应用-I/O字节流到字符流的适配器(对象的适配器模式)
Java I/O最关键的4个类是InputStream(输入字节流)、OutputStream(输出字节流)、Reader(输入字符流)、Writer(输出字符流),它们都是public abstract class类。
InputSream和OutputStream对于数据的传送是以字节Byte为单位的,而Reader和Writer对于数据的传送是以字符Character为单位的。所以我们看到java.io包中的类大体上可以分为两大类,一类是以Byte处理为主的Stream类,它们都是以XXXStream方式命名的;一类是以Character处理为主的Reader/Writer类,它们都是以XXXReader或XXXWriter的方式命名的。
表12-1展示了InputStream、OutputStream、Reader、Writer的各种实现的子类。可以分为3类:数据源类、数据流串联类、数据流过滤类。其中的数据源类是创建各种流的起始类,第二类和第三类都是对各自流实例的包装。
表12-1 输入/输出流类列表
输入字节流
输出字节流
输入字符流
输出字符流
抽象类
InputStream
OutputStream
Reader
Writer
转换类
InputStreamReader
OutputStreamWriter
第一类:数据源类
数组
ByteArrayInputStream
ByteArrayOutputStream
CharArrayReader
CharArrayWriter
文件
FileInputStream
FileOutputStream
FileReader
FileWriter
对象
ObjectInputStream
ObjectOutputStream
字符串
StringBufferInputStream
StringReader
StringWriter
第二类:数据流串联类
管道
PipeInputStream
PipeOutputStream
PipedReader
PipeWriter
序列
SequenceInputStream
第三类:数据流过滤类
过滤
FilterInputStream
FilterOutputStream
FilterReader
FilterWriter
缓冲
BufferedInputStream
BufferedOutputStream
BufferedReader
BufferedWriter
数据
DataInputStream
DataOutputStream
行号
LineNumberInputStream
LineNumberReader
推回
PushbackInputStream
PushbackReader
格式化
PrintStream
PrintWriter
关于该表中各种类及其原理的使用,详细参见《Java高手真经-Java核心编程技术》一书。
从上表可以看出,以字符为导向的reader/writer基本上有与之相对应的以字节为导向的stream。两个对应类实现的功能相同,只是在操作时的导向不同。如 CharArrayReader和ByteArrayInputStream的作用都是把内存中的一个缓冲区作为流使用,所不同的是前者每次从内存中读取一个字节的信息,而后者每次从内存中读取一个字符。
然而,字节流与字符流之间的区别却可以联系起来,这就是表中的两个类InputStreamReader和OutputStreamReader。InputStreamReader负责把字节输入流转换为字符输入流,OutputStreamReader负责把输出字节流转换为输出字符流。下面来看看如何进行转换。
1.字节输入流转换为字符输入流
InputStreamReader是字节流通向字符流的桥梁,它使用指定的charset读取字节并将其解码为字符。它拥有一个InputStream类型的变量,并继承了Reader,使用了对象的适配器模式,如图12-9所示。
根据InputStream的实例创建InputStreamReader的方法有4种:
InputStreamReader(InputStream in); //根据默认字符集创建 InputStreamReader(InputStream in, Charset cs); //使用给定字符集创建 InputStreamReader(InputStream in, CharsetDecoder dec); //使用给定字符集解码器创建 InputStreamReader(InputStream in, String charsetName); //使用指定字符集创建 后面的3个构造函数都指定了一个字符集,最后一个是最简单的,可以直接指定字符集的名称来创建,例如GB2312等。
每次调用InputStreamReader中的一个read()方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。共有3个可用的read()方法:
int read(); //读取单个字符 int read(char[] cbuf, int offset, int length); //将字符读入数组中的某一部分 boolean ready(); //判断此流是否已经准备好用于读取 InputStreamReader继承自Reader,因此该类的实例可以被各种输入字符流包装。为了达到最高效率,可以考虑在BufferedReader内包装InputStreamReader。例如程序12-20所示,我们首先创建了一个FileInputStream类的实例,然后转换为InputStreamReader对象is,最后使用BufferedReader进行包装。这样就可以将字节流转换为带缓冲功能的字符流。
程序12-20 TestInputStreamReader.java
public class TestInputStreamReader { public static void main(String[] args) { try { // 创建输入流 FileInputStream fis = new FileInputStream("D:/demo/test.txt"); InputStreamReader is = new InputStreamReader(fis); BufferedReader bis = new BufferedReader(is); // 从输入流读取数据 while (bis.ready()) { int c = bis.read(); System.out.print((char)c); } // 关闭输入流 bis.close(); is.close(); fis.close(); } catch (IOException e) { } } } 2.字节输出流转换为字符输出流
OutputStreamWriter是字符流通向字节流的桥梁,可使用指定的charset将要写入流中的字符编码成字节。因此,它拥有一个OutputStream类型的变量,并继承了Writer,使用了对象的适配器模式,如图12-10所示。
根据OutputStream的实例创建OutputStreamWriter的方法有4种:
OutputStreamReader(OutputStream out); //根据默认字符集创建 OutputStreamReader(OutputStream out, Charset cs); //使用给定字符集创建 OutputStreamReader(OutputStream out, CharsetDecoder dec); //使用给定字符集解码器创建 OutputStreamReader(OutputStream out, Stroutg charsetName); //使用指定字符集创建 后面的3个构造函数都制定了一个字符集,最后一个是最简单的,可以直接指定字符集的名称来创建,例如GB2312等。
每次调用write()方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。可以指定此缓冲区的大小,不过,默认的缓冲区对多数用途来说已足够大。注意,传递给write()方法的字符没有缓冲。共有3个可用的write()方法:
void write(char[] cbuf, int off, int len);//写入字符数组的某一部分 void write(int c);//写入单个字符 void write(String str, int off, int len);//写入字符串的某一部分 OutputStreamWriter继承自Writer,因此该类的实例可以被各种输出字符流包装。为了达到最高效率,可以考虑在BufferedWriter内包装OutputStreamWriter。例如程序12-21所示,我们首先创建了一个FileOutputStream类的实例,然后转换为OutputStreamReader对象os,最后使用BufferedWriter进行包装。这样就可以将字节流转换为带缓冲功能的字符流。
|