JAVA流---Writer类

450 阅读4分钟

「这是我参与11月更文挑战的第8天,活动详情查看:2021最后一次更文挑战

基本概念

Writer类是Java IO中所有Writer的基类。子类包括BufferedWriter和PrintWriter等等。

构造函数及方法

protected Writer() 创建一个新的字符流 writer,其关键部分将同步 writer 自身。
protected Writer(Object lock) 创建一个新的字符流 writer,其关键部分将同步给定的对象。
Writer  append(char c) 将指定字符添加到此 writer。
Writer  append(CharSequence csq) 将指定字符序列添加到此 writer。
Writer  append(CharSequence csq, int start, int end) 将指定字符序列的子序列添加到此 writer.Appendable。
abstract  void  close() 关闭此流,但要先刷新它。
abstract  void  flush() 刷新该流的缓冲。
void  write(char[] cbuf) 写入字符数组。
abstract  void  write(char[] cbuf, int off, int len) 写入字符数组的某一部分。
void  write(int c) 写入单个字符。
void  write(String str) 写入字符串。
void  write(String str, int off, int len) 写入字符串的某一部分。

Writer类图

CharArrayWriter 写入数据符

PipedWriter 实现了以数组作为目标的输出流

FilterWriter 字符过滤输出流

BufferedWriter 字符缓冲输出流

OutputStreamWriter 写入字符,同时可以设置编码集。

FileWriter 用来写入字符串到文件

PrintWriter 字符类型的打印输出流

CharArrayWriter 类

CharArrayReader 用于写入数据符,它继承于Writer,实现了以数组作为目标的输出流。

源码分析

public class CharArrayWriter extends Writer {
    // 字符数组缓冲
    protected char buf[];
    // 下一个字符的写入位置
    protected int count;
    // 构造函数:默认缓冲区大小是32
    public CharArrayWriter() {
        this(32);
    }
    // 构造函数:指定缓冲区大小是initialSize
    public CharArrayWriter(int initialSize) {
        if (initialSize < 0) {
            throw new IllegalArgumentException("Negative initial size: "
                                               + initialSize);
        }
        buf = new char[initialSize];
    }

    // 写入一个字符c到CharArrayWriter中
    public void write(int c) {
        synchronized (lock) {
            int newcount = count + 1;
            if (newcount > buf.length) {
                buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
            }
            buf[count] = (char)c;
            count = newcount;
        }
    }

    // 写入字符数组c到CharArrayWriter中。off是“字符数组b中的起始写入位置”,len是写入的长度
    public void write(char c[], int off, int len) {
        if ((off < 0) || (off > c.length) || (len < 0) ||
            ((off + len) > c.length) || ((off + len) < 0)) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return;
        }
        synchronized (lock) {
            int newcount = count + len;
            if (newcount > buf.length) {
                buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
            }
            System.arraycopy(c, off, buf, count, len);
            count = newcount;
        }
    }

    // 写入字符串str到CharArrayWriter中。off是“字符串的起始写入位置”,len是写入的长度
    public void write(String str, int off, int len) {
        synchronized (lock) {
            int newcount = count + len;
            if (newcount > buf.length) {
                buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
            }
            str.getChars(off, off + len, buf, count);
            count = newcount;
        }
    }

    // 将CharArrayWriter写入到“Writer对象out”中
    public void writeTo(Writer out) throws IOException {
        synchronized (lock) {
            out.write(buf, 0, count);
        }
    }

    // 将csq写入到CharArrayWriter中
    // 注意:该函数返回CharArrayWriter对象
    public CharArrayWriter append(CharSequence csq) {
        String s = (csq == null ? "null" : csq.toString());
        write(s, 0, s.length());
        return this;
    }

    // 将csq从start开始(包括)到end结束(不包括)的数据,写入到CharArrayWriter中。
    // 注意:该函数返回CharArrayWriter对象! 
    public CharArrayWriter append(CharSequence csq, int start, int end) {
        String s = (csq == null ? "null" : csq).subSequence(start, end).toString();
        write(s, 0, s.length());
        return this;
    }

    // 将字符c追加到CharArrayWriter中!
    // 注意:它与write(int c)的区别。append(char c)会返回CharArrayWriter对象。
    public CharArrayWriter append(char c) {
        write(c);
        return this;
    }

    // 重置
    public void reset() {
        count = 0;
    }

    // 将CharArrayWriter的全部数据对应的char[]返回
    public char toCharArray()[] {
        synchronized (lock) {
            return Arrays.copyOf(buf, count);
        }
    }

    // 返回CharArrayWriter的大小
    public int size() {
        return count;
    }

    public String toString() {
        synchronized (lock) {
            return new String(buf, 0, count);
        }
    }

    public void flush() { }

    public void close() { }
}
  • 通过CharArrayWriter()创建的CharArrayWriter对应的字符数组大小是32。

  • 通过CharArrayWriter(int size) 创建的CharArrayWriter对应的字符数组大小是size。

  • write(int oneChar)的作用将int类型的oneChar换成char类型,然后写入到CharArrayWriter中。

  • write(char[] buffer, int offset, int len) 是将字符数组buffer写入到输出流中,offset是从buffer中读取数据的起始偏移位置,len是读取的长度。

  • write(String str, int offset, int count) 是将字符串str写入到输出流中,offset是从str中读取数据的起始位置,count是读取的长度。

  • append(char c)的作用将char类型的c写入到CharArrayWriter中,然后返回CharArrayWriter对象。

  • append(CharSequence csq, int start, int end)的作用将csq从start开始(包括)到end结束(不包括)的数据,写入到CharArrayWriter中。

  • append(CharSequence csq)的作用将csq写入到CharArrayWriter中。

  • writeTo(OutputStream out) 将该“字符数组输出流”的数据全部写入到“输出流out”中。

PipedWriter类

PipedWriter 是字符管道输出流,它继承于Writer,它用于将当前线程的指定字符写入到与此线程对应的管道字符输入流中去、所以PipedReader、PipedWriter必须配套使用、缺一不可。管道字符输出流的本质就是调用PipedReader中的方法将字符或者字符数组写入到PipedReader中、这一点是与众不同的地方。所以pw中的方法很少也很简单、主要就是负责将传入的pr与本身绑定、配对使用、然后就是调用绑定的PipedReader的写入方法、将字符或者字符数组写入到PipedReader的缓存字符数组中。

源码分析

public class PipedWriter extends Writer {  
      
    //与此PipedWriter绑定的PipedReader  
    private PipedReader sink;  
  
    //标示此流是否关闭。  
    private boolean closed = false;  
  
    /** 
     * 根据传入的PipedReader构造pw、并将pr与此pw绑定 
     */  
    public PipedWriter(PipedReader snk)  throws IOException {  
        connect(snk);  
    }  
      
    /** 
     * 创建一个pw、在使用之前必须与一个pr绑定 
     */  
    public PipedWriter() {  
    }  
      
    /** 
     * 将此pw与一个pr绑定 
     */  
    public synchronized void connect(PipedReader snk) throws IOException {  
        if (snk == null) {  
            throw new NullPointerException();  
        } else if (sink != null || snk.connected) {  
            throw new IOException("Already connected");  
        } else if (snk.closedByReader || closed) {  
                throw new IOException("Pipe closed");  
        }  
              
        sink = snk;  
        snk.in = -1;  
        snk.out = 0;  
        snk.connected = true;  
    }  
  
    /** 
     * 将一个整数写入到与此pw绑定的pr的缓存字符数组buf中去 
     */  
    public void write(int c)  throws IOException {  
        if (sink == null) {  
            throw new IOException("Pipe not connected");  
        }  
        sink.receive(c);  
    }  
  
    /** 
     * 将cbuf的一部分写入pr的buf中去 
     */  
    public void write(char cbuf[], int off, int len) throws IOException {  
        if (sink == null) {  
            throw new IOException("Pipe not connected");  
        } else if ((off | len | (off + len) | (cbuf.length - (off + len))) < 0) {  
            throw new IndexOutOfBoundsException();  
        }  
        sink.receive(cbuf, off, len);  
    }  
  
    /** 
     * flush此流、唤醒pr中所有等待的方法。 
     */  
    public synchronized void flush() throws IOException {  
        if (sink != null) {  
                if (sink.closedByReader || closed) {  
                    throw new IOException("Pipe closed");  
                }              
                synchronized (sink) {  
                    sink.notifyAll();  
                }  
        }  
    }  
  
    /** 
     * 关闭此流。 
     */  
    public void close()  throws IOException {  
        closed = true;  
        if (sink != null) {  
            sink.receivedLast();  
        }  
    }  
}

PipedReader和PipedWriter与PipedInputStream和PipedOutputStream一样,都可以用于管道通信。PipedWriter是字符管道输出流,继承于Writer;PipedReader是字符管道输入流,继承于Reader,PipedWriter和PipedReader的作用是可以通过管道进行线程间的通讯。两者必须要配套使用。

FilterWriter类

FilterWriter是字符过滤输出流、与FilterOutputStream功能一样、只是简单重写了父类的方法、目的是为所有装饰类提供标准和基本的方法、要求子类必须实现核心方法、和拥有自己的特色。

源码分析

/** 
 * 本身是一个抽象类、只是对Writer中的方法进行了简单的重写、提供装饰类所具有的基本方法、 
 */  
public abstract class FilterWriter extends Writer {  
  
    /** 底层字符输出流*/  
    protected Writer out;  
  
    /** 使用传入的底层字符输出流创建FilterWriter*/  
    protected FilterWriter(Writer out) {  
        super(out);  
        this.out = out;  
    }  
  
    /** 将一个字符写入到out中*/  
    public void write(int c) throws IOException {  
        out.write(c);  
    }  
  
    /** 将一个字符数组的一部分写入到out中*/  
    public void write(char cbuf[], int off, int len) throws IOException {  
        out.write(cbuf, off, len);  
    }  
  
    /** 将一个字符串的一部分写入到out中*/  
    public void write(String str, int off, int len) throws IOException {  
        out.write(str, off, len);  
    }  
  
    /** flush此流*/  
    public void flush() throws IOException {  
        out.flush();  
    }  
    /** 关闭此流*/  
    public void close() throws IOException {  
        out.close();  
    }  
}
  • FileWriter.write一般用于写入或者追加文本

  • FileWriter.flush()用于刷新流的缓冲,这个时候还可以继续写入

  • FileWriter.close()用于释放资源写入文件,后面无法继续写数据,创建了FileWriter,如果不close()会占用系统资源,所以一定要及时关闭

BufferedWriter类

BufferedWriter 是缓冲字符输出流。它继承于Writer。它的作用是为其他字符输出流添加一些缓冲功能,能够提高效率。

源码分析

public class BufferedWriter extends Writer {
    // 输出流对象
    private Writer out;

    // 保存“缓冲输出流”数据的字符数组
    private char cb[];

    // nChars 是cb缓冲区中字符的总的个数
    // nextChar 是下一个要读取的字符在cb缓冲区中的位置
    private int nChars, nextChar;

    // 默认字符缓冲区大小
    private static int defaultCharBufferSize = 8192;

    // 行分割符
    private String lineSeparator;

    // 构造函数,传入“Writer对象”,默认缓冲区大小是8k
    public BufferedWriter(Writer out) {
        this(out, defaultCharBufferSize);
    }

    // 构造函数,传入“Writer对象”,指定缓冲区大小是sz
    public BufferedWriter(Writer out, int sz) {
        super(out);
        if (sz <= 0)
            throw new IllegalArgumentException("Buffer size <= 0");
        this.out = out;
        cb = new char[sz];
        nChars = sz;
        nextChar = 0;

        lineSeparator = java.security.AccessController.doPrivileged(
            new sun.security.action.GetPropertyAction("line.separator"));
    }

    // 确保“BufferedWriter”是打开状态
    private void ensureOpen() throws IOException {
        if (out == null)
            throw new IOException("Stream closed");
    }

    // 对缓冲区执行flush()操作,将缓冲区的数据写入到Writer中
    void flushBuffer() throws IOException {
        synchronized (lock) {
            ensureOpen();
            if (nextChar == 0)
                return;
            out.write(cb, 0, nextChar);
            nextChar = 0;
        }
    }

    // 将c写入到缓冲区中。先将c转换为char,然后将其写入到缓冲区。
    public void write(int c) throws IOException {
        synchronized (lock) {
            ensureOpen();
            // 若缓冲区满了,则清空缓冲,将缓冲数据写入到输出流中。
            if (nextChar >= nChars)
                flushBuffer();
            cb[nextChar++] = (char) c;
        }
    }

    // 返回a,b中较小的数
    private int min(int a, int b) {
        if (a < b) return a;
        return b;
    }

    // 将字符数组cbuf写入到缓冲中,从cbuf的off位置开始写入,写入长度是len。
    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;
            }

            if (len >= nChars) {
                /* If the request length exceeds the size of the output buffer,
                   flush the buffer and then write the data directly.  In this
                   way buffered streams will cascade harmlessly. */
                flushBuffer();
                out.write(cbuf, off, len);
                return;
            }

            int b = off, t = off + len;
            while (b < t) {
                int d = min(nChars - nextChar, t - b);
                System.arraycopy(cbuf, b, cb, nextChar, d);
                b += d;
                nextChar += d;
                if (nextChar >= nChars)
                    flushBuffer();
            }
        }
    }

    // 将字符串s写入到缓冲中,从s的off位置开始写入,写入长度是len。
    public void write(String s, int off, int len) throws IOException {
        synchronized (lock) {
            ensureOpen();

            int b = off, t = off + len;
            while (b < t) {
                int d = min(nChars - nextChar, t - b);
                s.getChars(b, b + d, cb, nextChar);
                b += d;
                nextChar += d;
                if (nextChar >= nChars)
                    flushBuffer();
            }
        }
    }

    // 将换行符写入到缓冲中
    public void newLine() throws IOException {
        write(lineSeparator);
    }

    // 清空缓冲区数据
    public void flush() throws IOException {
        synchronized (lock) {
            flushBuffer();
            out.flush();
        }
    }

    public void close() throws IOException {
        synchronized (lock) {
            if (out == null) {
                return;
            }
            try {
                flushBuffer();
            } finally {
                out.close();
                out = null;
                cb = null;
            }
        }
    }
}

OutputStreamWriter类

OutputStreamWriter是Writer的子类,将输出的字符流变为字节流,即将一个字符流的输出对象变为字节流的输出对象。它是字符流通向字节流的桥梁,可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。

源码分析

/** 
 * 字符输出流、是使用指定的编码或者时系统默认的编码将字节转换成字符。 
 * 是OutputStream转换成Writer的桥梁、也是PrintStream、能够在构造的时候指定编码的关键。 
 * 实际上是将OutputStream传递给了StreamEncoder来操作out 
 */  
public class OutputStreamWriter extends Writer {  
  
    //通过StreamEncoder se进行字节与字符的转码。  
    private final StreamEncoder se;  
  
    /** 
     * 用指定的编码将OutputStream out转换成OutputStreamWriter。 
     */  
    public OutputStreamWriter(OutputStream out, String charsetName)  
    throws UnsupportedEncodingException  
    {  
        // 调用父类Writer的构造方法创建一个新的字符流 writer,其关键部分将同步给自身。  
        super(out);  
        if (charsetName == null)  
            throw new NullPointerException("charsetName");  
        //初始化StreamEncoder se.  
        se = StreamEncoder.forOutputStreamWriter(out, this, charsetName);  
    }  
  
    /** 
     * 使用默认编码创建osw。 
     */  
    public OutputStreamWriter(OutputStream out) {  
    super(out);  
    try {  
        se = StreamEncoder.forOutputStreamWriter(out, this, (String)null);  
    } catch (UnsupportedEncodingException e) {  
        throw new Error(e);  
        }  
    }  
  
    /** 
     * 使用指定的字符集创建osw 
     */  
    public OutputStreamWriter(OutputStream out, Charset cs) {  
    super(out);  
    if (cs == null)  
        throw new NullPointerException("charset");  
    se = StreamEncoder.forOutputStreamWriter(out, this, cs);  
    }  
  
    /** 
     * 使用指定的字符编码创建osw 
     */  
    public OutputStreamWriter(OutputStream out, CharsetEncoder enc) {  
    super(out);  
    if (enc == null)  
        throw new NullPointerException("charset encoder");  
    se = StreamEncoder.forOutputStreamWriter(out, this, enc);  
    }  
  
    /** 
     * 获取字符流使用的字符集、或者编码 
     */  
    public String getEncoding() {  
        return se.getEncoding();  
    }  
  
    /** 
     * 将osw中的字符flush到底层OutputStream中、此方法不包含flush底层out中的字节 
     */  
    void flushBuffer() throws IOException {  
        se.flushBuffer();  
    }  
  
    /** 
     * 将字节转换成字符后、向out中写入一个字符(字符的编码在构造OutputStreamWriter时指定或者使用默认编码 
     */  
    public void write(int c) throws IOException {  
        se.write(c);  
    }  
  
    /** 
     * 将cbuf中的一部分写入到out中 
     */  
    public void write(char cbuf[], int off, int len) throws IOException {  
        se.write(cbuf, off, len);  
    }  
  
    /** 
     * 将str一部分写入到out中 
     */  
    public void write(String str, int off, int len) throws IOException {  
        se.write(str, off, len);  
    }  
  
    /** 
     * flush此流、实际上是调用StreamEncoder的flush方法、 
     */  
    public void flush() throws IOException {  
        se.flush();  
    }  
  
    /** 
     * 关闭StreamEncoder、即关闭OutputSteramWriter. 
     */  
    public void close() throws IOException {  
        se.close();  
    }  
}