基本概念
InputStreamReader / OutputStreamWriter 这两个类分别继承了 Reader与 Writer,它们提供了从字节流到字符流的转换。
在转换的过程中,既可以使用平台默认的字符编码也可以手动指定。如果需要更多地控制编码过程,则应该使用 CharsetEncoder 类。
真正负责转换工作的是 StreamDecoder 类
实例探究
下面先来一个简单使用例子
public class Test {
private static final String TEMPFILE = "E:" + File.separator + "1.txt";
public static void main(String[] args) throws Exception {
writer(TEMPFILE);
read(TEMPFILE);
// 输出内容:你好
}
private static void read(String path) throws Exception {
File file = new File(path);
Reader reader = new InputStreamReader(new FileInputStream(file));
char[] buffer = new char[1024];
int count = 0;
while ((count = reader.read(buffer)) != -1) {
System.out.println(new String(buffer, 0, count));
}
}
private static void writer(String path) throws IOException {
File file = new File(path);
Writer writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
writer.write("你好");
writer.close();
}
}
源码分析
1.InputStreamReader
类结构图
成员变量
//从字节到字符的转换真正依靠的是这个类
private final StreamDecoder sd;
关于构造函数,发现该类总共定义了 4 个构造函数。关于 ②③ 的方式其实差不多,因为通过下面的例子可以清楚看到 charsetName 与 Charset 关系。
String charsetName ="UTF-8";
Charset = Charset.forName(charsetName);
观察代码会发现其实真正负责转换工作的是 StreamDecoder 类中的 forInputStreamReader 方法,这里只是简单的调用了这个方法而已。
//①构造函数,使用默认的字符集
public InputStreamReader(InputStream in) {
super(in);
try {
sd = StreamDecoder.forInputStreamReader(in, this, (String) null);
} catch (UnsupportedEncodingException e) {
// The default encoding should always be available
throw new Error(e);
}
}
//②构造函数,使用指定的字符集名称
public InputStreamReader(InputStream in, String charsetName) throws UnsupportedEncodingException {
super(in);
if (charsetName == null)
throw new NullPointerException("charsetName");
sd = StreamDecoder.forInputStreamReader(in, this, charsetName);
}
//②构造函数,使用指定的字符集
public InputStreamReader(InputStream in, Charset cs) {
super(in);
if (cs == null)
throw new NullPointerException("charset");
sd = StreamDecoder.forInputStreamReader(in, this, cs);
}
//④构造函数,使用给定字符集解码器
public InputStreamReader(InputStream in, CharsetDecoder dec) {
super(in);
if (dec == null)
throw new NullPointerException("charset decoder");
sd = StreamDecoder.forInputStreamReader(in, this, dec);
}
剩余方法, 发现实现的核心都是围绕着 StreamDecoder 类。
public String getEncoding() {
return sd.getEncoding();
}
public int read() throws IOException {
return sd.read();
}
public int read(char cbuf[], int offset, int length) throws IOException {
return sd.read(cbuf, offset, length);
}
public boolean ready() throws IOException {
return sd.ready();
}
public void close() throws IOException {
sd.close();
}
2.OutputStreamWriter
结构图
成员变量,同上,这里不再阐述
同样的定义了 4 个构造函数
public OutputStreamWriter(OutputStream out) {
super(out);
try {
se = StreamEncoder.forOutputStreamWriter(out, this, (String) null);
} catch (UnsupportedEncodingException e) {
throw new Error(e);
}
}
public OutputStreamWriter(OutputStream out, String charsetName) throws UnsupportedEncodingException {
super(out);
if (charsetName == null){
throw new NullPointerException("charsetName");
}
se = StreamEncoder.forOutputStreamWriter(out, this, charsetName);
}
public OutputStreamWriter(OutputStream out, Charset cs) {
super(out);
if (cs == null){
throw new NullPointerException("charset");
}
se = StreamEncoder.forOutputStreamWriter(out, this, cs);
}
public OutputStreamWriter(OutputStream out, CharsetEncoder enc) {
super(out);
if (enc == null){
throw new NullPointerException("charset encoder");
}
se = StreamEncoder.forOutputStreamWriter(out, this, enc);
}
剩余的方法
public String getEncoding() {
return se.getEncoding();
}
//刷新输出缓冲数组到底层系统(不是刷新流本身),这是不设置为私有方法,是为让 PrintStream 能调用到
void flushBuffer() throws IOException {
se.flushBuffer();
}
public void write(int c) throws IOException {
se.write(c);
}
public void write(char cbuf[], int off, int len) throws IOException {
se.write(cbuf, off, len);
}
public void write(String str, int off, int len) throws IOException {
se.write(str, off, len);
}
public void flush() throws IOException {
se.flush();
}
public void close() throws IOException {
se.close();
}