JavaIo(3)-Writer

设计Reader和Writer继承层次结构主要是为了国际化。老的IO流继承层次结构仅支持8位的字节流,并且不能很好的处理16位的Unicode字符,所以添加Reader和Writer继承层次结构就是为了在所有的IO操作中都支持Unicode。

然而在某些场合,我们不得不面临着字符编码的问题,即字符和字节之间按照什么编码方式(GBK,UTF-8,ISO-8859-1等)来编解码的问题。这时我们将用到OutputStreamWriter和InputStreamReader这两个字符流,它们分别是字符通向字节的桥梁和字节通向字符的桥梁。
关于“装饰者模式”,字符流Reader和Writer的类继承层次结构沿用与字节流相同的思想,但是并不完全相同。造成这种差异的原因是因为类的组织形式不同,尽管BufferedOutputStream是FilterOutputStream的子类,但是BufferedWriter并不是FilterWriter的子类,但它仍然是其他Writer类的装饰器类。尽管FilterWriter是抽象类,但是没有任何子类,把它放在那里也只是把它作为一个占位符,或者仅仅让我们不会对它所在的地方产生疑惑。
框架

Writer源码:

package java.io;
/**
 * Writer是写入字符流的抽象类。定义了流的最基本的功能。
 * 子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。
 * 但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。
 */
public abstract class Writer implements Appendable, Closeable, Flushable {

    /**
     * 字符buffer
     */
    private char[] writeBuffer;

    /**
     * 字符buffer的默认大小
     */
    private static final int WRITE_BUFFER_SIZE = 1024;

    /**
     * 用于同步针对此流的操作的对象
     */
    protected Object lock;

    /**
     * 构造方法1,使用本类对象锁
     */
    protected Writer() {
        this.lock = this;
    }

    /**
     * 构造方法2,使用指定的锁对象
     */
    protected Writer(Object lock) {
        if (lock == null) {
            throw new NullPointerException();
        }
        this.lock = lock;
    }

    /**
     * 写入单个字符。要写入的字符包含在给定整数值的 16 个低位中,16 高位被忽略。
     * 用于支持高效单字符输出的子类应重写此方法。 
     */
    public void write(int c) throws IOException {
        synchronized (lock) {  // write()方法内是同步代码块
            if (writeBuffer == null){
                writeBuffer = new char[WRITE_BUFFER_SIZE];
            }
            writeBuffer[0] = (char) c;
            write(writeBuffer, 0, 1);
        }
    }

    /**
     * 写入字符数组。
     */
    public void write(char cbuf[]) throws IOException {
        write(cbuf, 0, cbuf.length);
    }

    /**
     * 写入字符数组的某一部分。子类需实现该方法
     */
    abstract public void write(char cbuf[], int off, int len) throws IOException;

    /**
     * 写入字符串。
     */
    public void write(String str) throws IOException {
        write(str, 0, str.length());
    }

    /**
     * 写入字符串的某一部分。 
     */
    public void write(String str, int off, int len) throws IOException {
        synchronized (lock) {
            char cbuf[];
            if (len <= WRITE_BUFFER_SIZE) {
                if (writeBuffer == null) {
                    writeBuffer = new char[WRITE_BUFFER_SIZE];
                }
                cbuf = writeBuffer;
            } else {    // Don't permanently allocate very large buffers.
                cbuf = new char[len];
            }
            str.getChars(off, (off + len), cbuf, 0);
            write(cbuf, 0, len);
        }
    }

    /**
     * 将指定字符序列添加到此 writer
     */
    public Writer append(CharSequence csq) throws IOException {
        if (csq == null)
            write("null");
        else
            write(csq.toString());
        return this;
    }

    /**
     * 将指定字符序列的子序列添加到此 writer。
     */
    public Writer append(CharSequence csq, int start, int end) throws IOException {
        CharSequence cs = (csq == null ? "null" : csq);
        write(cs.subSequence(start, end).toString());
        return this;
    }

    /**
     * 将指定字符添加到此 writer。
     */
    public Writer append(char c) throws IOException {
        write(c);
        return this;
    }

    /**
     * 刷新该流的缓冲。
     */
    abstract public void flush() throws IOException;

    /**
     * 关闭此流,但要先刷新它。
     * 在关闭该流之后,再调用 write() 或 flush() 将导致抛出 IOException。
     */
    abstract public void close() throws IOException;

}

二,OutputStreamWriter源码

package java.io;

import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import sun.nio.cs.StreamEncoder;

/**
 * OutputStreamWriter是字符流通向字节流的桥梁:可使用指定的charset将要写入流中的字符编码成字节。
 * 它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
 * 每次调用 write() 方法都会导致在给定字符(或字符集)上调用编码转换器。
 * 在写入底层输出流之前,得到的这些字节将在缓冲区中累积。
 * 为了获得最高效率,可考虑将OutputStreamWriter包装到BufferedWriter中,以避免频繁调用转换器。
 * 例如:Writer out = new BufferedWriter(new OutputStreamWriter(System.out));
 */

public class OutputStreamWriter extends Writer {

    /**
     * 本类所实现的由字符到字节的编码严重依赖StreamEncoder类及其方法。
     * 本文并不打算讲解StreamEncoder类,只介绍其方法达到什么目的。
     */
    private final StreamEncoder se;

    /**
     * 创建使用指定字符集的 OutputStreamWriter。 
     */
    public OutputStreamWriter(OutputStream out, String charsetName)
        throws UnsupportedEncodingException
    {
        super(out);
        if (charsetName == null)
            throw new NullPointerException("charsetName");
        se = StreamEncoder.forOutputStreamWriter(out, this, charsetName);
    }

    /**
     * 创建使用默认字符编码的 OutputStreamWriter
     */
    public OutputStreamWriter(OutputStream out) {
        super(out);
        try {
            se = StreamEncoder.forOutputStreamWriter(out, this, (String)null);
        } catch (UnsupportedEncodingException e) {
            throw new Error(e);
        }
    }

    /**
     * 创建使用给定字符集的 OutputStreamWriter。
     */
    public OutputStreamWriter(OutputStream out, Charset cs) {
        super(out);
        if (cs == null)
            throw new NullPointerException("charset");
        se = StreamEncoder.forOutputStreamWriter(out, this, cs);
    }

    /**
     * 创建使用给定字符集编码器的 OutputStreamWriter。
     */
    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();
    }

    /**
     * 刷新buffer
     */
    void flushBuffer() throws IOException {
        se.flushBuffer();
    }

    /**
     * 写入单个字符。 
     */
    public void write(int c) throws IOException {
        se.write(c);
    }

    /**
     * 写入字符数组的某一部分。 
     */
    public void write(char cbuf[], int off, int len) throws IOException {
        se.write(cbuf, off, len);
    }

    /**
     * 写入字符串的某一部分。 
     */
    public void write(String str, int off, int len) throws IOException {
        se.write(str, off, len);
    }

    /**
     * 刷新该流的缓冲。 
     */
    public void flush() throws IOException {
        se.flush();
    }

    /**
     * 关闭此流,但要先刷新它。
     */
    public void close() throws IOException {
        se.close();
    }
}

BufferedWriter源码

package java.io;


/**
 * 将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
 * 可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。
 * 该类提供了 newLine() 方法,它使用平台自己的行分隔符概念。
 * 
 */

public class BufferedWriter extends Writer {

    private Writer out; // 持有父类对象

    private char cb[];  // buffer
    private int nChars, nextChar;

    private static int defaultCharBufferSize = 8192; // 默认buffer大小

    /**
     * 换行符
     */
    private String lineSeparator;

    /**
     * 创建一个使用默认大小输出缓冲区的缓冲字符输出流。
     */
    public BufferedWriter(Writer out) {
        this(out, defaultCharBufferSize);
    }

    /**
     * 创建一个使用给定大小输出缓冲区的新缓冲字符输出流。 
     */
    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"));
    }

    /** 检查流是否打开 */
    private void ensureOpen() throws IOException {
        if (out == null)
            throw new IOException("Stream closed");
    }

    /**
     * 刷新缓冲区。
     */
    void flushBuffer() throws IOException {
        synchronized (lock) {
            ensureOpen();
            if (nextChar == 0)
                return;
            out.write(cb, 0, nextChar);
            nextChar = 0;
        }
    }

    /**
     * 写入单个字符。
     * 底层调用了Writer类型的write()方法,但是本类方法为其进行了"装饰",即加入了缓冲区技术。
     */
    public void write(int c) throws IOException {
        synchronized (lock) {
            ensureOpen();
            if (nextChar >= nChars)
                flushBuffer();
            cb[nextChar++] = (char) c;
        }
    }

    /**
     * 内部方法
     */
    private int min(int a, int b) {
        if (a < b) return a;
        return b;
    }

    /**
     * 写入字符数组的某一部分。 同样对老write()方法进行了"缓冲区装饰"
     */
    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();
            }
        }
    }

    /**
     * 写入字符串的某一部分。同样对老write()方法进行了"缓冲区装饰"。
     */
    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();
            }
        }
    }

    /**
     * 写入一个行分隔符。行分隔符字符串由系统属性 line.separator 定义
     */
    public void newLine() throws IOException {
        write(lineSeparator);
    }

    /**
     * 刷新该流的缓冲。 
     */
    public void flush() throws IOException {
        synchronized (lock) {
            flushBuffer();
            out.flush();
        }
    }

    /**
     * 关闭此流,但要先刷新它。
     */
    @SuppressWarnings("try")
    public void close() throws IOException {
        synchronized (lock) {
            if (out == null) {
                return;
            }
            try (Writer w = out) {
                flushBuffer();
            } finally {
                out = null;
                cb = null;
            }
        }
    }
}

一般来说,FileWriter, FileReader的操作:

File file = new File("testWriter.txt");
String a = "这是字符型";
FileWriter out = new FileWriter( file );
out.write( a,0,a.length() );
out.write("\r\n");
out.close();

char[] b = new char[10];
FileReader in = new FileReader( file );
in.read( b );
System.out.println(b);
in.close();

更常用的是:

FileWriter outOne = new FileWriter( file ,true );
BufferedWriter out2 = new BufferedWriter( outOne );
String a2 = "缓冲区字符输入更高效";
out2.write( a2 );
out2.newLine();
out2.write("测试换行行不行");
out2.close();

BufferedReader in2 = new BufferedReader( new FileReader(file) );
String str;
while( (str = in2.readLine()) != null ) {
    System.out.println(str);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值