[重学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();
        }

你可能感兴趣的:(Java基础增强)