java IO(七):FileWriter和OutputStreamWriter和StreamEncoder

其实这三个输出流,就是对应那三个输入流的。

和那三个输入流一样,这三个流在字符输出上也密切相关。

一、FileWriter

和输入流一样,也是只有几个对应文件系统的构造方法,这里不再赘述。

二、OutputStreamWriter

和输入流一样,也是通过传入的OutputStream流对StreamEncoder进行初始化,append在OutputStream流中已指定。

三、StreamEncoder

这才是字符输出流的关键类(同理在实际使用中也最好使用BufferedWriter进行包装)

这个类和StreamDecoder长得非常类似,我们来比较一下:

1)属性域

    
StreamEncoder
   private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192; //确保流是打开状态否则无法输入输出 private volatile boolean isOpen; //字符集 private Charset cs; //编码器 private CharsetEncoder encoder; //字节缓冲区 private ByteBuffer bb; //输出流 private final OutputStream out; //信道,对于输入流,缓冲区从信道中取数据,对于输出流,缓冲区输出内容到信道(应该是这样,信道这个概念还未理解) private WritableByteChannel ch; //为保证读入的字符不乱吗,则每次要读入两个字符 private boolean haveLeftoverChar; private char leftoverChar; //字符缓冲区——专用于左侧字符操作 private CharBuffer lcb;
  
StreamDecoder
  //定义两个表示字节缓冲区大小的整型常量,最小为32,默认为8192 private static final int MIN_BYTE_BUFFER_SIZE = 32; private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192; //表示流是否被打开的布尔型变量 private volatile boolean isOpen; //在read里用到,read方法返回一个字符,但实际上是读取2个字符,剩余的一个字符会保存到leftoverChar里,haveLeftoverChar表示是否有这个剩余多出来的字符。 private boolean haveLeftoverChar; private char leftoverChar; //这个和下面的ReadableByteChannel意思是信道,但还不知道有什么用 private static volatile boolean channelsAvailable = true; //字符集 private Charset cs; //解码器 private CharsetDecoder decoder; //字节缓冲对象 private ByteBuffer bb; //字节输入流 private InputStream in; private ReadableByteChannel ch;

2)构造方法

    private StreamEncoder(OutputStream var1, Object var2, Charset var3) {
        this(var1, var2, var3.newEncoder().onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE));
    }

    private StreamEncoder(OutputStream var1, Object var2, CharsetEncoder var3) {
        super(var2);
        this.isOpen = true;
        this.haveLeftoverChar = false;
        this.lcb = null;
        this.out = var1;
        this.ch = null;
        this.cs = var3.charset();
        this.encoder = var3;
        if (this.ch == null) {
            this.bb = ByteBuffer.allocate(8192);
        }

    }

    private StreamEncoder(WritableByteChannel var1, CharsetEncoder var2, int var3) {
        this.isOpen = true;
        this.haveLeftoverChar = false;
        this.lcb = null;
        this.out = null;
        this.ch = var1;
        this.cs = var2.charset();
        this.encoder = var2;
        this.bb = ByteBuffer.allocate(var3 < 0 ? 8192 : var3);
    }

和StreamDecoder区别不大,不再赘述

3)forOutputStreamWriter()

    public static StreamEncoder forOutputStreamWriter(OutputStream var0, Object var1, String var2) throws UnsupportedEncodingException {
        String var3 = var2;
        if (var2 == null) {
            var3 = Charset.defaultCharset().name();
        }

        try {
            if (Charset.isSupported(var3)) {
                return new StreamEncoder(var0, var1, Charset.forName(var3));
            }
        } catch (IllegalCharsetNameException var5) {
        }

        throw new UnsupportedEncodingException(var3);
    }

    public static StreamEncoder forOutputStreamWriter(OutputStream var0, Object var1, Charset var2) {
        return new StreamEncoder(var0, var1, var2);
    }

    public static StreamEncoder forOutputStreamWriter(OutputStream var0, Object var1, CharsetEncoder var2) {
        return new StreamEncoder(var0, var1, var2);

也和StreamDecoder区别不大。

4)write()

相较StreamDecoder的read()方法,write()方法要简单许多,就是直接判断offset和len的合法性,然后调用implWrite()

public void write(int var1) throws IOException {
        char[] var2 = new char[]{(char)var1};
        this.write((char[])var2, 0, 1);
    }

    public void write(char[] var1, int var2, int var3) throws IOException {
        synchronized(this.lock) {
            this.ensureOpen();
            if (var2 >= 0 && var2 <= var1.length && var3 >= 0 && var2 + var3 <= var1.length && var2 + var3 >= 0) {
                if (var3 != 0) {
                    this.implWrite(var1, var2, var3);
                }
            } else {
                throw new IndexOutOfBoundsException();
            }
        }
    }

    public void write(String var1, int var2, int var3) throws IOException {
        if (var3 < 0) {
            throw new IndexOutOfBoundsException();
        } else {
            char[] var4 = new char[var3];
            var1.getChars(var2, var2 + var3, var4, 0);
            this.write((char[])var4, 0, var3);
        }
    }

上下两个方法本质就是调用中间的write(char[] var1,int var2,int var3)

对于最上面的方法,是将单个字符转成字符数组,然后传入中间方法;对于下面的方法,是将字符串转成字符数组,再传入中间方法。

中间方法var1便是那个字符数组,var2可以理解为offset,var3可以理解为length。

5)implWrite(char[] cbuf, int offset, int length)

void implWrite(char[] var1, int var2, int var3) throws IOException {
        CharBuffer var4 = CharBuffer.wrap(var1, var2, var3);
        if (this.haveLeftoverChar) {
            this.flushLeftoverChar(var4, false);
        }

        while(var4.hasRemaining()) {
            CoderResult var5 = this.encoder.encode(var4, this.bb, false);
            if (var5.isUnderflow()) {
                assert var4.remaining() <= 1 : var4.remaining();

                if (var4.remaining() == 1) {
                    this.haveLeftoverChar = true;
                    this.leftoverChar = var4.get();
                }
                break;
            }

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

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

    }

java IO(七):FileWriter和OutputStreamWriter和StreamEncoder

flushLeftoverChar()和writeBytes()将在(八)中说到。

上一篇:终于明白 Java 为什么要加 final 关键字了!


下一篇:【redis】set后value出现空格