InputStreamReader的作用就是将字节输入流变为字符输入流,底层的实现是靠StreamDecoder完成的。
public class InputStreamReader extends Reader {
private final StreamDecoder sd;
public InputStreamReader(InputStream in) {
super(in);
try {
sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
} 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);
}
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();
}
}
StreamDecoder在jdk源码中并没有开源,不过通过相应版本的openJDK可以看到StreamDecoder的源码。
public int read() throws IOException {
return read0();
}
//读取单个字符
private int read0() throws IOException {
synchronized (lock) {
// Return the leftover char, if there is one
if (haveLeftoverChar) {
haveLeftoverChar = false;
return leftoverChar;
}
// Convert more bytes
char cb[] = new char[2];//至少读取2个字符
int n = read(cb, 0, 2);
switch (n) {
case -1:
return -1;
case 2://保留多读取的字符,以便下次使用
leftoverChar = cb[1];
haveLeftoverChar = true;
// FALL THROUGH
case 1://返回读取到的第一个字符
return cb[0];
default:
assert false : n;
return -1;
}
}
}
//读取字符数组
public int read(char cbuf[], int offset, int length) throws IOException {
int off = offset;
int len = length;
synchronized (lock) {
ensureOpen();
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
}
if (len == 0)
return 0;
int n = 0;
//返回此前缓存的单个字符
if (haveLeftoverChar) {
// Copy the leftover char into the buffer
cbuf[off] = leftoverChar;
off++; len--;
haveLeftoverChar = false;
n = 1;
if ((len == 0) || !implReady())
// Return now if this is all we can produce w/o blocking
return n;
}
//读取单个字符
if (len == 1) {
// Treat single-character array reads just like read()
int c = read0();
if (c == -1)
return (n == 0) ? -1 : n;
cbuf[off] = (char)c;
return n + 1;
}
return n + implRead(cbuf, off, off + len);//读取底层输入流的函数
}
}
在read函数中,要求至少读取2个字符,为什么这样设置呢?这跟Unicode字符集及utf-16编码有关了,具体解释可以参考Character的API文档及参考文章使用 Java 语言进行 Unicode 代理编程。
OutputStreamWriter使用StreamEncoder进行字符到字节的编码,原理与上面类似。