[重学Java基础][Java IO流][Part.6-2]StreamDecoder和StreamEncoder

[重学Java基础][JavaIO流][Part.6-2]StreamDecoder和StreamEncoder

StreamDecoder

概述

这个类是sun.nio包下的类
在之前的甲骨文官方的JDK中是看不到源码的 只能下载OpenJDK查看
或者在grepcode这个网站在线查看


openJDK在线源码

但是java9 开始 甲骨文官方的jdk也是基于openjdk进行开发
所以jdk1.9中是可以看到此类的原源码的

源码解析

成员变量

最小字节缓冲区大小 
private static final int MIN_BYTE_BUFFER_SIZE = 32;
默认字节缓冲区大小
private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192;
多线程可见量 校验字节流是否打开
private volatile boolean isOpen = true;
是否保存左字符标志
为了保证读入的字符不乱码 则每次读入不能少于两个字符
如果只想要返回一个字符 那么可以保存左侧字符 等到下次返回
private boolean haveLeftoverChar = false;
左侧位字符
private char leftoverChar;
字符集
private Charset cs;
字符解码器 可以将一个字节序列按照特定的字符集转换成一个16位的Unicode序列  
private CharsetDecoder decoder;
字节缓冲
private ByteBuffer bb;
被转换的字节输入流
private InputStream in;
可读取字节信道
private ReadableByteChannel ch;

构造方法 通过传入字节输入流 锁对象 字符集构造StreamDecoder对象

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) {
    super(lock);
    this.cs = dec.charset();
    this.decoder = dec;

    // 调用NIO方法 如果输入数据速度较快时启用(输入数据是文件输入流)
    if (false && in instanceof FileInputStream) {
        ch = getChannel((FileInputStream)in);
        if (ch != null)
            bb = ByteBuffer.allocateDirect(DEFAULT_BYTE_BUFFER_SIZE);
    }
    if (ch == null) {
        this.in = in;
        this.ch = null;
        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();
}

专门构造InputStreamReader的构造方法

入参 输入流InputStream in 同步锁对象 Object lock 指定编码String charsetName
public static StreamDecoder forInputStreamReader(InputStream in,
                                                 Object lock,
                                                 String charsetName)
    throws UnsupportedEncodingException
{
    String csn = charsetName;
    if (csn == null)
        csn = Charset.defaultCharset().name();
    try {
        if (Charset.isSupported(csn))
            return new StreamDecoder(in, lock, Charset.forName(csn));
    } catch (IllegalCharsetNameException x) { }
    throw new UnsupportedEncodingException (csn);
}

类似上面 不过是直接指定编码字符集对象Charset  少了一些判断
public static StreamDecoder forInputStreamReader(InputStream in,
                                                 Object lock,
                                                 Charset cs)
{
    return new StreamDecoder(in, lock, cs);
}

public static StreamDecoder forInputStreamReader(InputStream in,
                                                 Object lock,
                                                 CharsetDecoder dec)
{
    return new StreamDecoder(in, lock, dec);
}

读取方法 内部调用了私有read0()方法

 public int read() throws IOException {
        return read0();
    }

@SuppressWarnings("fallthrough")
private int read0() throws IOException {
    synchronized (lock) {

        //  如果只有一个(左侧高位)字符 则直接返回 并haveLeftoverChar置位false
        if (haveLeftoverChar) {
            haveLeftoverChar = false;
            return leftoverChar;
        }

        // 包装更多的字节数据
        char cb[] = new char[2];
        int n = read(cb, 0, 2);
        switch (n) {
        读取完毕 返回-1
            case -1:
                return -1;
            case 2:
                leftoverChar = cb[1];
                haveLeftoverChar = true;
            case 1:
                return cb[0];
            default:
                assert false : n;
                return -1;
        }
    }
}

StreamEncoder

源码解析

成员变量

默认字节缓冲大小
private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192;
多线程可见量 用于多线程下校验流是否关闭
private volatile boolean closed;

字符集
private Charset cs;
字符解码器 可以将一个字节序列按照特定的字符集转换成一个16位的Unicode序列  
private CharsetDecoder decoder;
字节缓冲
private ByteBuffer bb;

输入流
private final OutputStream out;
写入信道
private WritableByteChannel ch;

是否保存左字符标志
为了保证读入的字符不乱码 则每次读入不能少于两个字符
如果只想要返回一个字符 那么可以保存左侧字符 等到下次返回
private boolean haveLeftoverChar = false;
private char leftoverChar;
private CharBuffer lcb = null;

成员方法

其中比较重要的就是构造OutputStream的构造方法 和StreamDecoder大同小异

public static StreamEncoder forOutputStreamWriter(OutputStream out,
                                                  Object lock,
                                                  String charsetName)
    throws UnsupportedEncodingException
{
    String csn = charsetName;
    if (csn == null)
        csn = Charset.defaultCharset().name();
    try {
        if (Charset.isSupported(csn))
            return new StreamEncoder(out, lock, Charset.forName(csn));
    } catch (IllegalCharsetNameException x) { }
    throw new UnsupportedEncodingException (csn);
}

public static StreamEncoder forOutputStreamWriter(OutputStream out,
                                                  Object lock,
                                                  Charset cs)
{
    return new StreamEncoder(out, lock, cs);
}

public static StreamEncoder forOutputStreamWriter(OutputStream out,
                                                  Object lock,
                                                  CharsetEncoder enc)
{
    return new StreamEncoder(out, lock, enc);
}

写入方法 虽然一系列复杂的判断 但最终是调用了writeBytes()方法写入的

public void write(int c) throws IOException {
    char cbuf[] = new char[1];
    cbuf[0] = (char) c;
    write(cbuf, 0, 1);
}

public void write(char cbuf[], int off, int len) throws IOException {
    synchronized (lock) {
        ensureOpen();
        if ((off < 0) || (off > cbuf.length) || (len < 0) ||
            ((off + len) > cbuf.length) || ((off + len) < 0)) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return;
        }
        implWrite(cbuf, off, len);
    }
}

public void write(String str, int off, int len) throws IOException {
    /* Check the len before creating a char buffer */
    if (len < 0)
        throw new IndexOutOfBoundsException();
    char cbuf[] = new char[len];
    str.getChars(off, off + len, cbuf, 0);
    write(cbuf, 0, len);
}

public void write(CharBuffer cb) throws IOException {
    int position = cb.position();
    try {
        synchronized (lock) {
            ensureOpen();
            implWrite(cb);
        }
    } finally {
        cb.position(position);
    }
}
write()方法调用implWrite()方法 implWrite()方法调用writeBytes()方法
void implWrite(char cbuf[], int off, int len)
    throws IOException
{
    CharBuffer cb = CharBuffer.wrap(cbuf, off, len);
    implWrite(cb);
}

内部一系列判断 就是判断流是否读完 最后剩余不够两个字节
(英文字符占一个字节 中文字符占两个字节)如何处理
void implWrite(CharBuffer cb)
    throws IOException
{
    if (haveLeftoverChar) {
        flushLeftoverChar(cb, false);
    }

    while (cb.hasRemaining()) {
        CoderResult cr = encoder.encode(cb, bb, false);
        if (cr.isUnderflow()) {
            assert (cb.remaining() <= 1) : cb.remaining();
            if (cb.remaining() == 1) {
                haveLeftoverChar = true;
                leftoverChar = cb.get();
            }
            break;
        }
        if (cr.isOverflow()) {
            assert bb.position() > 0;
            writeBytes();
            continue;
        }
        cr.throwException();
    }
}

真正的写入方法writeBytes()

private void writeBytes() throws IOException {
    bb.flip();
    int lim = bb.limit();
    int pos = bb.position();
    此处使用了assert 断言关键字 
    assert (pos <= lim);
    int rem = (pos <= lim ? lim - pos : 0);

        if (rem > 0) {
    if (ch != null) {
        if (ch.write(bb) != rem)
            assert false : rem;
    } else {
        out.write(bb.array(), bb.arrayOffset() + pos, rem);
    }
    }
    bb.clear();
    }
阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011863951/article/details/80004958
个人分类: Java基础增强
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭