java IO(九):StreamEncoder补遗


这边再说三个方法:flushLeftoverChar(),writeBytes(),close()

一、flushLeftoverChar()

    private void flushLeftoverChar(CharBuffer var1, boolean var2) throws IOException {
        if (this.haveLeftoverChar || var2) {
            if (this.lcb == null) {
                this.lcb = CharBuffer.allocate(2);
            } else {
                this.lcb.clear();
            }

            if (this.haveLeftoverChar) {
                this.lcb.put(this.leftoverChar);
            }

            if (var1 != null && var1.hasRemaining()) {
                this.lcb.put(var1.get());
            }

            this.lcb.flip();

            while(this.lcb.hasRemaining() || var2) {
                CoderResult var3 = this.encoder.encode(this.lcb, this.bb, var2);
                if (var3.isUnderflow()) {
                    if (this.lcb.hasRemaining()) {
                        this.leftoverChar = this.lcb.get();
                        if (var1 != null && var1.hasRemaining()) {
                            this.flushLeftoverChar(var1, var2);
                        }

                        return;
                    }
                    break;
                }

                if (var3.isOverflow()) {
                    assert this.bb.position() > 0;

                    this.writeBytes();
                } else {
                    var3.throwException();
                }
            }

            this.haveLeftoverChar = false;
        }
    }

var1是字符缓冲区,var2调用时为false,leftoverChar与haveLeftoverChar为该类属性域。下面为流程图:

二、writeBytes():

此方法才是真正把数据输出至计算机的方法(之前只是存在缓冲区里,并未真正输出到计算机本地)

 private void writeBytes() throws IOException {
        this.bb.flip();
        int var1 = this.bb.limit();
        int var2 = this.bb.position();

        assert var2 <= var1;

        int var3 = var2 <= var1 ? var1 - var2 : 0;
        if (var3 > 0) {
            if (this.ch != null) {
                assert this.ch.write(this.bb) == var3 : var3;
            } else {
                this.out.write(this.bb.array(), this.bb.arrayOffset() + var2, var3);
            }
        }

        this.bb.clear();
    }

这里先把字节缓冲区翻转(因为之前都是从字符缓冲区里往其中放数据,现在要取出来输出),然后判断bb中position和limit位置的合法性,若无错,则利用通道或是不使用通道输出(有通道则优先使用通道),输出后将bb清空以便下一次填充。

三、close():

其实我们可以看到,在implwrite()的有些情况和flushLeftoverChar中最后都没有直接使用writeBytes()进行输出,那么这时方法调用完后编码的字符还在字节缓冲区中,并未输出至本地,所以,使用write类输出流时,最后一定不能忘记关闭输出流,输出流的close()方法会将字节缓冲区中余下的数据输出至本地文件。如果不调用,则输出可能少数据或根本不会进行输出!下面举一个例子:

public class Test  {
    public static void main(String[] args) {
        File file = new File("D:\\img\\test.txt");
        try {
            OutputStreamWriter writer = new FileWriter(file,true);
            String s = "中文";
            writer.write(s);
            //writer.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        catch (IOException e){
            e.printStackTrace();
        }
        System.out.println();
    }
}

这里若将close注释掉,则运行后:

可以看到文本文档是空的,而调用close()后,在看该文本文档:

这时候便正常输出了。

public void close() throws IOException {
        synchronized(this.lock) {
            if (this.isOpen) {
                this.implClose();
                this.isOpen = false;
            }
        }
    }

而close()方法本质是调用了implClose():

void implClose() throws IOException {
        this.flushLeftoverChar((CharBuffer)null, true);

        try {
            while(true) {
                CoderResult var1 = this.encoder.flush(this.bb);
                if (var1.isUnderflow()) {
                    if (this.bb.position() > 0) {
                        this.writeBytes();
                    }

                    if (this.ch != null) {
                        this.ch.close();
                    } else {
                        this.out.close();
                    }

                    return;
                }

                if (var1.isOverflow()) {
                    assert this.bb.position() > 0;

                    this.writeBytes();
                } else {
                    var1.throwException();
                }
            }
        } catch (IOException var2) {
            this.encoder.reset();
            throw var2;
        }
    }

可以看到该方法里又进行了一遍编码判断并全都调用了writeBytes()方法,把字节缓冲区里所有的内容全部输出,这就是为什么一定要调用close()的原因了。(该方法同时也会将leftoverChar输出,在方法开头有判断)