InputStreamReader和StreamDecoder的源码和使用方法详细分析(windows操作系统,JDK8)
InputStreamReader——FileInputStream和FileReader之间的桥梁
计算机存储的单位是字节,尽管我们平时在操作系统(此处以windows操作系统为例)中使用.txt文件保存了汉字这样的字符,但是对计算机而言,这些.txt文件中的汉字其实是以字节形式存在的,所以,当使用者使用字节输入流(比如FileInputStream)来读取文件的时候其实是一个一个的单字节读取(1个字节可以表示28=256个符号,比如ASCII码就是用1个字节表示的编码方案 ,覆盖了英文中的所有符号),但是不同字符集解码成字符的时候需要不同个数的字节(比如中文,1个字节就无法表示完所有的中文汉字,因为中文汉字有5000+个,远远超过了28=256覆盖的范围),因此使用字节输入流(比如FileInputStream)来读取中文的时候会出现乱码,InputStreamReader.class就可以把字节输入流读取的字节进行缓冲然后再通过字符集解码成字符返回。
换句话说,InputStreamReader.class是从字节流到字符流的桥接器,InputStreamReader.class可以使用指定的字符集读取字节并将它们解码为字符。 它使用的字符集可以通过名称指定,也可以明确指定,或者可以接受操作系统平台的默认字符集。每次调用一个InputStreamReader的read()函数都可能导致从底层字节输入流中读取一个或多个字节。 为了实现字节到字符的有效转换,InputStreamReader.class可以从底层字节输入流中提取比满足当前读取操作所需的更多字节。
read()函数会尝试尽量从底层字节输入流中读取2个字符到字符缓冲区中,注意这里是尽量,比如遇到文件的最后字符时,就只能读取到1个字符,因此每次read()函数读取的字节数是不定的。
InputStreamReader的UML关系图,如下所示:
image
InputStreamReader.class的源码,如下所示:
package java.io;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import sun.nio.cs.StreamDecoder;
public class InputStreamReader extends Reader {
private final StreamDecoder sd;
//使用InputStream类型字节输入流对象构造InputStreamReader对象,Reader.class::lock变量指向的对象(锁)也是这个InputStream类型字节输入流对象,使用的字符集为UTF-8(默认值)
public InputStreamReader(InputStream in) {
super(in);//设置Reader.class::lock变量指向的对象(锁)也是这个InputStream类型字节输入流对象
try {
sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
} catch (UnsupportedEncodingException e) {
//如果有异常,抛出一个Error,直接退出系统进程
// The default encoding should always be available
throw new Error(e);
}
}
//使用InputStream类型字节输入流对象构造InputStreamReader对象,Reader.class::lock变量指向的对象(锁)也是这个InputStream类型字节输入流对象,使用的字符集为自己用字符串指定的字符集
public InputStreamReader(InputStream in, String charsetName)
throws UnsupportedEncodingException
{
super(in);//设置Reader.class::lock变量指向的对象(锁)也是这个InputStream类型字节输入流对象
if (charsetName == null)
throw new NullPointerException("charsetName");
sd = StreamDecoder.forInputStreamReader(in, this, charsetName);
}
//使用InputStream类型字节输入流对象构造InputStreamReader对象,Reader.class::lock变量指向的对象(锁)也是这个InputStream类型字节输入流对象,使用的字符集为自己用Charset对象指定的字符集
public InputStreamReader(InputStream in, Charset cs) {
super(in);//设置Reader.class::lock变量指向的对象(锁)也是这个InputStream类型字节输入流对象
if (cs == null)
throw new NullPointerException("charset");
sd = StreamDecoder.forInputStreamReader(in, this, cs);
}
//使用InputStream类型字节输入流对象构造InputStreamReader对象,Reader.class::lock变量指向的对象(锁)也是这个InputStream类型字节输入流对象,使用的字符集为自己用CharsetDecoder对象指定的字符集
public InputStreamReader(InputStream in, CharsetDecoder dec) {
super(in);
if (dec == null)
throw new NullPointerException("charset decoder");
sd = StreamDecoder.forInputStreamReader(in, this, dec);
}
//调用StreamDecoder.class::getEncoding()函数获取StreamDecoder使用的解码方案(字符集),这些解码方案(字符集)包括但不限于ISO_8859_1,US_ASCII,Unicode(包括UTF-8)...
public String getEncoding() {
return sd.getEncoding();
}
//调用StreamDecoder.class::read()函数返回1个字符(如果还有字符的话)
public int read() throws IOException {
return sd.read();
}
//调用StreamDecoder.class::read(char cbuf[], int offset, int length) 函数一次性读取length个字符到字符数组char[] cbuf的[offset,offset+length)索引位置,并返回实际读取到的字符数量(如果有足够多的字符的话)
public int read(char cbuf[], int offset, int length) throws IOException {
return sd.read(cbuf, offset, length);
}
//调用StreamDecoder.class::ready()函数
//如果字节缓冲区ByteBuffer bb不为空,或者底层字节输入流或者文件中有字节可供读取,则返回true
public boolean ready() throws IOException {
return sd.ready();
}
//调用StreamDecoder.class::close()函数
public void close() throws IOException {
sd.close();
}
}
二、StreamDecoder——InputStreamReader中实际将字节解码的JDK内部类
InputStreamReader.class中的所有函数本质都是在调用StreamDecoder.class中的函数,包括InputStreamReader的构造函数都是在调用StreamDecoder.class::forInputStreamReader()函数,StreamDecoder的函数中会根据指定的字符集创造指定的字节解码器,字节解码器的作用就是解码字节成字符。
同时StreamDecoder.class这个类是在sun.nio.cs包下的类,因此Oracle的官方JDK中无法看到相关的源码,必须要通过OpenJDK来查看,OpenJDK的github地址如下:https://github.com/openjdk/jdk/blob/jdk-25%2B20/src/java.base/share/classes/sun/nio/cs/StreamEncoder.java
StreamDecoder的UML关系图,如下所示:
image
StreamDecoder.class的源码,如下所示:
package sun.nio.cs;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.io.Reader;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
import java.util.Arrays;
public class StreamDecoder extends Reader {
// ByteBuffer(字节缓冲区) bb的大小,分为以下2中情况:
//场景一:当使用Channels对象创建自己这个StreamDecoder对象的时候,会对实参中的minBufferCap(mbc)变量做判断,又分为以下3种情况:
// a、当minBufferCap(mbc)<0时,会为ByteBuffer(字节缓冲区) bb分配一个8192(DEFAULT_BYTE_BUFFER_SIZE)的长度;
// b、当0<=minBufferCap(mbc)<32时,会为ByteBuffer(字节缓冲区) bb分配一个32(MIN_BYTE_BUFFER_SIZE)的长度;
// c、当32<=minBufferCap(mbc)时,为ByteBuffer(字节缓冲区) bb分配的长度就是minBufferCap(mbc)这个实参;
//场景二:当使用InputStreamReader 对象创建自己这个StreamDecoder对象的时候,没有指定ByteBuffer(字节缓冲区)长度的实参,会直接为ByteBuffer(字节缓冲区) bb分配一个8192(DEFAULT_BYTE_BUFFER_SIZE)的长度
private static final int MIN_BYTE_BUFFER_SIZE = 32;
private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192;
//标记这个StreamDecoder是否开启,false表示开启,可以使用StreamDecoder.class::read()函数和StreamDecoder.class::ready()函数了,true表示关闭,不能再使用StreamDecoder.class::read()函数和StreamDecoder.class::ready()函数了
private volatile boolean closed;
//如果StreamDecoder已经被boolean closed变量标记为关闭(true)了,则不能再执行StreamDecoder.class::read()函数和StreamDecoder.class::ready()函数了,否则会抛出一个IOException(“Stream closed”)
private void ensureOpen() throws IOException {
if (closed)
throw new IOException(“Stream closed”);
}
// In order to handle surrogates properly we must never try to produce
// fewer than two characters at a time. If we're only asked to return one
// character then the other is saved here to be returned later.
//
// 特别为read()函数准备(可以更好的处理代理字符),read()函数只要求返回1个字符,但是实际上是读取了2个字符,多读取的1个字符会被赋值(缓存)到char leftoverChar变量中,而boolean haveLeftoverChar变量只是用来标记char leftoverChar中是否有缓存的字符
//这个思路类似于BufferedReader中用boolean skipLF跳过换行符\r\n(在windows操作系统下面),但是又并不完全相同,BufferedReader中的所有数据都保存在char[] cb(缓冲区数组)中
private boolean haveLeftoverChar = false;
private char leftoverChar;
//提供给InputStreamReader对象创建自己这个StreamDecoder对象的工厂函数
public static StreamDecoder forInputStreamReader(InputStream in,
Object lock,
String charsetName)
throws UnsupportedEncodingException
{
try {
return new StreamDecoder(in, lock, Charset.forName(charsetName));
} catch (IllegalCharsetNameException | UnsupportedCharsetException x) {
throw new UnsupportedEncodingException (charsetName);
}
}
//提供给InputStreamReader对象创建自己这个StreamDecoder对象的工厂函数
public static StreamDecoder forInputStreamReader(InputStream in,
Object lock,
Charset cs)
{
return new StreamDecoder(in, lock, cs);
}
//提供给InputStreamReader对象创建自己这个StreamDecoder对象的工厂函数
public static StreamDecoder forInputStreamReader(InputStream in,
Object lock,
CharsetDecoder dec)
{
return new StreamDecoder(in, lock, dec);
}
// Factory for java.nio.channels.Channels.newReader
//提供给Channels对象创建自己这个StreamDecoder对象的工厂函数
public static StreamDecoder forDecoder(ReadableByteChannel ch,
CharsetDecoder dec,
int minBufferCap)
{
return new StreamDecoder(ch, dec, minBufferCap);
}
//接下来的函数都是与 InputStreamReader 中的函数一一对应的,换句话说,接下来的函数都是通过 InputStreamReader 中对应功能的函数(可能名称不对应,但是功能一定对应)来调用的
//所有同步和状态/参数检查都在这些函数中完成;下面定义的具体流解码器子类无需进行此类检查。
// -- Public methods corresponding to those in InputStreamReader --
// All synchronization and state/argument checking is done in these public
// methods; the concrete stream-decoder subclasses defined below need not
// do any such checking.
//返回这个StreamDecoder使用的解码方案(字符集),这些解码方案(字符集)包括但不限于ISO_8859_1,US_ASCII,Unicode(包括UTF-8)...
public String getEncoding() {
if (isOpen())
return encodingName();
return null;
}
//对应InputStreamReader.class::read()函数,返回单个字符,实际调用下面的read0()函数
public int read() throws IOException {
return read0();
}
//@SuppressWarnings("fallthrough")可以压制(即消除)编译器对 case 2 分支缺少中断语句(return、break、throw...等)的报警
//函数内部做了线程同步,如果有足够字符的话,一次性从read(char[] cbuf, int offset, int length)函数中读取2个字符
@SuppressWarnings("fallthrough")
private int read0() throws IOException {
synchronized (lock) {
if (haveLeftoverChar) {
//如果char leftoverChar变量中有被赋值(缓存)的字符char,则直接返回,并把boolean haveLeftoverChar标记改为false表示char leftoverChar变量中有被赋值(缓存)的字符已经被读取了
haveLeftoverChar = false;
return leftoverChar;
}
// 创建了长度为2的字符数组char[] cb
char[] cb = new char[2];
// 调用下面的read(char[] cbuf, int offset, int length)函数,返回读取的字符个数
int n = read(cb, 0, 2);
switch (n) {
case -1:// 若read(char[] cbuf, int offset, int length)函数返回-1,代表读到末尾
return -1; //直接返回-1
case 2:// 若read(char[] cbuf, int offset, int length)函数返回2,代表读到了2个字符
leftoverChar = cb[1];//将char[] cb数组中第2个字符(索引1位置)放到char leftoverChar变量中暂时缓存
haveLeftoverChar = true;//并把boolean haveLeftoverChar标记改为true表示char leftoverChar变量中有被赋值(缓存)的字符
// FALL THROUGH
case 1:// 若read(char[] cbuf, int offset, int length)函数返回1,代表读到了1个字符
return cb[0];//实际执行的时候,如果read(char[] cbuf, int offset, int length)函数的返回值为2,char[] cb数组中有2个字符,这里会被case2穿透,直接返回char[] cb数组中的第1个字符(索引0位置)
//详情看小标题x.x.x
default:
assert false : n;//实际编译后,这里会被编译器优化到case2之前,详情看小标题x.x.x
return -1;
}
}
}
//如果有足够多字符的话,一次性读取length个字符到字符数组char[] cbuf的[offset,offset+length)索引位置,并返回实际读取到的字符数量
//函数内部做了线程同步
public int read(char[] cbuf, int offset, int length) throws IOException {
synchronized (lock) {
//先在这个函数的调用栈上复制offset为off、length为len
int off = offset;
int len = length;
ensureOpen();
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {//char[]数组cbuf的[offset,offset+length)(左闭右开)索引位置是否有越界的检查
throw new IndexOutOfBoundsException();//越界的话,抛出一个IndexOutOfBoundsException
}
if (len == 0)
return 0;//如果len==0,结束本次函数调用,并返回0
//实际读取到的字符数量
int n = 0;
// 如果char leftoverChar变量中有被赋值(缓存)的字符char,则boolean haveLeftoverChar变量为true
if (haveLeftoverChar) {
// 将char leftoverChar变量中被赋值(缓存)的字符char先放到字符数组char[] cbuf的off(offset)索引位置
cbuf[off] = leftoverChar;
off++; len--;//然后将off(offset)索引位置右移1位,将len-=1
haveLeftoverChar = false;//并把boolean haveLeftoverChar标记改为false表示char leftoverChar变量中有被赋值(缓存)的字符了
n = 1;//设置n=1
if ((len == 0) || !implReady())
// Return now if this is all we can produce w/o blocking
//如果这已经是StreamDecoder在不阻塞的情况下能读取到字符数组char[] cbuf中的全部字符了,就立即返回。
return n;
}
if (len == 1) {
//如果此时len==1,则表示接下来只需要向字符数组char[] cbuf中再读取1个字符,那么直接调用read0()函数即可,此时len==1分为以下2中情况:
//场景一:要读取到字符数组char[] cbuf中的字符数量length=2,同时char leftoverChar变量中被赋值(缓存)了1个字符
//场景二:要读取到字符数组char[] cbuf中的字符数量length=1,并且char leftoverChar变量中没有缓存字符
int c = read0();//read0()函数一次只能读取1个字符
if (c == -1)//c==-1表示从已经从read0()函数中读取不到任何字符了
return (n == 0) ? -1 : n;//返回实际读取到字符数组char[] cbuf中的字符数量n或者-1
cbuf[off] = (char)c;//如果还能从read0()函数中读取到字符,则将读取到的这个字符保存到字符数组char[] cbuf中
return n + 1;//累加1到实际读取到字符数组char[] cbuf中的字符数量n上面,并返回
}
// Read remaining characters
//执行完上面的代码到这里的时候,off和len有以下2种可能:
//可能1:char leftoverChar变量中没有被赋值(缓存)的字符char,并且len>1(要填充到字符数组char[] cbuf中的字符数量length>1);
//可能2:char leftoverChar变量中有被赋值(缓存)的字符char,并且len>2(要填充到字符数组char[] cbuf中的字符数量length>2)。
//那么执行implRead()函数向字符数组char[] cbuf的[off,off + len)索引位置填充len个字符,并返回实际填充的字符数量nr,举例如下:
//假如当int offset的实参为0,int length的实参为5时,如果之前满足可能1,则此时[off,off + len)为[0,5),返回5或者-1,如果之前满足可能2,则此时[off,off + len)为[1,5),返回4或者-1
int nr = implRead(cbuf, off, off + len);
// At this point, n is either 1 if a leftover character was read,
// or 0 if no leftover character was read. If n is 1 and nr is -1,
// indicating EOF, then we don't return their sum as this loses data.
//此时nr有以下2种可能得值,-1或者实际向字符数组char[] cbuf的[off,off + len)索引位置填充的字符数量nr,当nr=-1时,n只有以下2种可能:
//可能a:之前char leftoverChar变量中没有被赋值(缓存)的字符char,n=0,此时return -1
//可能b:之前char leftoverChar变量中有被赋值(缓存)的字符char,n=1,此时return 1
//当nr≠=-1时,表示的确向字符数组char[] cbuf的[off,off + len)索引位置填充了nr个字符,return n+nr(0+nr或者1+nr)
return (nr < 0) ? (n == 1 ? 1 : nr) : (n + nr);
}
}
public boolean ready() throws IOException {
synchronized (lock) {
ensureOpen();
return haveLeftoverChar || implReady();
}
}
//关闭这个StreamDecoder中成员变量引用的字节输入流和其它资源,并且设置boolean closed=true标记这个StreamDecoder已经关闭了,不能再使用所有重载的StreamDecoder.class::read()函数和StreamDecoder.class::ready()函数了
//函数内部做了线程同步
public void close() throws IOException {
synchronized (lock) {
if (closed)
return;
try {
implClose();
} finally {
closed = true;
}
}
}
private boolean isOpen() {
return !closed;
}
public void fillZeroToPosition() throws IOException {
synchronized (lock) {
Arrays.fill(bb.array(), bb.arrayOffset(),
bb.arrayOffset() + bb.position(), (byte)0);
}
}
// -- Charset-based stream decoder impl --
private final Charset cs;
//StreamDecoder中真正用来将ByteBuffer bb中的字节解码成不同字符的工具(ByteBuffer bb中的字节也是从字节输入流InputStream in中获取到的),可以解码的字符集(解码方案)包括但不限于ISO_8859_1,US_ASCII,Unicode(包括UTF-8)...
private final CharsetDecoder decoder;
//字节输入流InputStream in中的字节被CharsetDecoder decoder解码成字符之前,会先读取到ByteBuffer bb中缓存
private final ByteBuffer bb;
//字节输入流,真正通过StreamDecoder转换成字符的字节都是从这个字节输入流中获取的,不过将这些字节转换成字符之前会先读取到ByteBuffer bb中缓存
private final InputStream in;
//只有通过Channels对象创建自己这个StreamDecoder对象的时候才会使用到这个变量,通InputStreamReader对象创建自己这个StreamDecoder对象的时候用不到这个变量
private final ReadableByteChannel ch;
//构造函数
StreamDecoder(InputStream in, Object lock, Charset cs) {
this(in, lock,
cs.newDecoder()
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE));
}
//构造函数
StreamDecoder(InputStream in, Object lock, CharsetDecoder dec) {
//设置Reader.class::lock变量指向的对象(锁)是创建自己这个StreamDecoder对象的InputStreamReader对象
super(lock);
this.cs = dec.charset();
this.decoder = dec;
this.in = in;
this.ch = null;
this.bb = ByteBuffer.allocate(DEFAULT_BYTE_BUFFER_SIZE);
bb.flip(); // So that bb is initially empty
}
//构造函数
StreamDecoder(ReadableByteChannel ch, CharsetDecoder dec, int mbc) {
this.in = null;
this.ch = ch;
this.decoder = dec;
this.cs = dec.charset();
this.bb = ByteBuffer.allocate(mbc < 0
? DEFAULT_BYTE_BUFFER_SIZE
: (mbc < MIN_BYTE_BUFFER_SIZE
? MIN_BYTE_BUFFER_SIZE
: mbc));
bb.flip();
}
//最终调用的方法,利用底层字节输入流,读取字节到字节缓冲区ByteBuffer bb中
private int readBytes() throws IOException {
bb.compact();
try {
if (ch != null) {
// Read from the channel
int n = ch.read(bb);
if (n < 0)
return n;
} else {
// Read from the input stream, and then update the buffer
// 从输入流中读取,然后更新字节缓冲区ByteBuffer bb
int lim = bb.limit();
int pos = bb.position();
assert (pos <= lim);
int rem = (pos <= lim ? lim - pos : 0);
int n = in.read(bb.array(), bb.arrayOffset() + pos, rem);
if (n < 0)
return n;
if (n == 0)
throw new IOException("Underlying input stream returned zero bytes");
assert (n <= rem) : "n = " + n + ", rem = " + rem;
bb.position(pos + n);
}
} finally {
//即使抛出 IOException 也要翻转,否则字节缓冲区ByteBuffer会有问题
bb.flip();//反转字节缓冲区中的所有字节
}
//计算读取到字节缓冲区ByteBuffer中的字节数量,并返回
int rem = bb.remaining();
assert (rem != 0) : rem;
return rem;
}
//如果有足够多字符的话,一次性填充end-off个字符到字符数组char[] cbuf的[off,end)索引位置,并返回实际填充到字符数组char[] cbuf中的字符数量
int implRead(char[] cbuf, int off, int end) throws IOException {
//要填充到字符数组char[] cbuf中的字符数量必须>1
assert (end - off > 1);
//将字符数组char[] cbuf包装到字符缓冲区CharBuffer cb中,包装到缓冲区之后,字符缓冲区CharBuffer cb中修改的内容会同步修改到字符数组char[] cbuf中
//可以将CharBuffer对象理解为一个数组,管理的索引位置为[off,end)区间的end - off个字符
CharBuffer cb = CharBuffer.wrap(cbuf, off, end - off);
if (cb.position() != 0) {
//将字符缓冲区CharBuffer cb管理的索引区间和要填充的字符数组char[] cbuf进行对齐,确保cb[0]=cbuf[0ff]
cb = cb.slice();
}
//eof=false表示还没有读到字节输入流(或者叫文件)的末尾,字节输入流或者文件中还有字节可以提供给StreamDecoder来进行转换
boolean eof = false;
for (;;) {
//从字节缓冲区ByteBuffer bb中解码尽可能多的字节,将结果写入给定的字符缓冲区CharBuffer cb中,也就是字符数组char[] cbuf中用于读取字符
CoderResult cr = decoder.decode(bb, cb, eof);
if (cr.isUnderflow()) {
if (eof)
break;
if (!cb.hasRemaining())
//检查字符缓冲区CharBuffer cb中是否还有剩余空间,若没有则跳出循环
break;
if ((cb.position() > 0) && !inReady())
//字符缓冲区CharBuffer cb存在内容时,跳出循环,以便返回读取到的字符数
break; // Block at most once
//读取字节到字节缓冲区ByteBuffer bb
int n = readBytes();
if (n < 0) {
//若读取字节到字节缓冲区ByteBuffer bb时返回了-1,代表着已到了字节输入流(或者叫文件)的末尾,则不会有其它字节输入了
eof = true;
if ((cb.position() == 0) && (!bb.hasRemaining()))
//如果此时字符缓冲区CharBuffer cb和字节缓冲区ByteBuffer bb中都没有数据了,则跳出循环
break;
}
continue;
}
if (cr.isOverflow()) {
//当从字节缓冲区ByteBuffer bb中解码的字节太多导致溢出时,字符缓冲区CharBuffer cb必须有解码的字符,否则这里的断言就会报错
assert cb.position() > 0;
//溢出时也跳出循环
break;
}
cr.throwException();
}
if (eof) {
// ## Need to flush decoder
//如果已经到了字节输入流(或者叫文件)的末尾,则重置解码器,清除所有内部状态。
decoder.reset();
}
if (cb.position() == 0) {
if (eof) {
return -1;
}
assert false;
}
//返回字符缓冲区CharBuffer cb的当前位置,代表着实际读取或者填充到字符数组char[] cbuf中的字符数量
return cb.position();
}
String encodingName() {
return ((cs instanceof HistoricallyNamedCharset)
? ((HistoricallyNamedCharset)cs).historicalName()
: cs.name());
}
private boolean inReady() {
try {
return (((in != null) && (in.available() > 0))
|| (ch instanceof FileChannel)); // ## RBC.available()?
} catch (IOException x) {
return false;
}
}
boolean implReady() {
return bb.hasRemaining() || inReady();
}
void implClose() throws IOException {
if (ch != null) {
ch.close();
} else {
in.close();
}
}
}
2.1、构造函数和可以解码的字符集(解码方案)
StreamDecoder的构造函数从来不单独对外提供,所以,使用者包括JDK的内部类必须要同过forInputStreamReader()、forDecoder()这些工厂函数来创建StreamDecoder.class的实例。同时,StreamDecoder.class在sun.nio.cs包中,使用者无法引入这个包,所以,使用者也无法在自己的代码中创建StreamDecoder的实例,因此,StreamDecoder.class中提供的工厂函数forInputStreamReader()、forDecoder()只是提供给JDK的内部类InputStreamReader.class和Channels.class来使用的。如下所示:
public class StreamDecoder extends Reader {
…省略部分代码…
//提供给InputStreamReader对象创建自己这个StreamDecoder对象的工厂函数
public static StreamDecoder forInputStreamReader(InputStream in,
Object lock,
String charsetName)
throws UnsupportedEncodingException
{
try {
return new StreamDecoder(in, lock, Charset.forName(charsetName));
} catch (IllegalCharsetNameException | UnsupportedCharsetException x) {
throw new UnsupportedEncodingException (charsetName);
}
}
//提供给InputStreamReader对象创建自己这个StreamDecoder对象的工厂函数
public static StreamDecoder forInputStreamReader(InputStream in,
Object lock,
Charset cs)
{
return new StreamDecoder(in, lock, cs);
}
//提供给InputStreamReader对象创建自己这个StreamDecoder对象的工厂函数
public static StreamDecoder forInputStreamReader(InputStream in,
Object lock,
CharsetDecoder dec)
{
return new StreamDecoder(in, lock, dec);
}
// Factory for java.nio.channels.Channels.newReader
//提供给Channels对象创建自己这个StreamDecoder对象的工厂函数
public static StreamDecoder forDecoder(ReadableByteChannel ch,
CharsetDecoder dec,
int minBufferCap)
{
return new StreamDecoder(ch, dec, minBufferCap);
}
//构造函数
StreamDecoder(InputStream in, Object lock, Charset cs) {
this(in, lock,
cs.newDecoder()
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE));
}
//构造函数
StreamDecoder(InputStream in, Object lock, CharsetDecoder dec) {
//设置Reader.class::lock变量指向的对象(锁)是创建自己这个StreamDecoder对象的InputStreamReader对象
super(lock);
this.cs = dec.charset();
this.decoder = dec;
this.in = in;
this.ch = null;
this.bb = ByteBuffer.allocate(DEFAULT_BYTE_BUFFER_SIZE);
bb.flip(); // So that bb is initially empty
}
//构造函数
StreamDecoder(ReadableByteChannel ch, CharsetDecoder dec, int mbc) {
this.in = null;
this.ch = ch;
this.decoder = dec;
this.cs = dec.charset();
this.bb = ByteBuffer.allocate(mbc < 0
? DEFAULT_BYTE_BUFFER_SIZE
: (mbc < MIN_BYTE_BUFFER_SIZE
? MIN_BYTE_BUFFER_SIZE
: mbc));
bb.flip();
}
...省略部分代码...
}
2.1.1、可以解码的字符集(解码方案)
StreamDecoder.class中将字节输入流中的字节解码成字符的过程是通过CharsetDecoder decoder这个工具来完成的,如下所示:
...省略部分代码...
//StreamDecoder中真正用来将ByteBuffer bb中的字节解码成不同字符的工具(ByteBuffer bb中的字节也是从字节输入流InputStream in中获取到的),可以解码的字符集(解码方案)包括但不限于ISO_8859_1,US_ASCII,Unicode(包括UTF-8)...
private final CharsetDecoder decoder;
//如果有足够多字符的话,一次性填充end-off个字符到字符数组char[] cbuf的[off,end)索引位置,并返回实际填充到字符数组char[] cbuf中的字符数量
int implRead(char[] cbuf, int off, int end) throws IOException {
...省略部分代码...
//eof=false表示还没有读到字节输入流(或者叫文件)的末尾,字节输入流或者文件中还有字节可以提供给StreamDecoder来进行转换
boolean eof = false;
for (;;) {
//从字节缓冲区ByteBuffer bb中解码尽可能多的字节,将结果写入给定的字符缓冲区CharBuffer cb中,也就是字符数组char[] cbuf中用于读取字符
CoderResult cr = decoder.decode(bb, cb, eof);
...省略部分代码...
StreamDecoder.class可以解码的字符集(解码方案)包括但不限于ISO_8859_1,US_ASCII,Unicode(包括UTF-8)…,
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)