其实这三个输出流,就是对应那三个输入流的。
和那三个输入流一样,这三个流在字符输出上也密切相关。
一、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(); } } }
flushLeftoverChar()和writeBytes()将在(八)中说到。