Java8 I/O源码-Reader与Writer

本文介绍了Java8中字符输入流的抽象类Reader和字符输出流的抽象类Writer的源码分析,包括它们与字节流的区别,并探讨了Reader和Writer的子类通常如何提高效率和添加功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

字节输入流的学习告一段落了,接下来的一段时间学习字符输入流。本文学习字符输入流的抽象类Reader与和字符输出流的抽象类Writer。

Reader

Reader是字符输入流的抽象类。子类必须实现的方法只有read(char[], int, int) 和close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。

下面学习下Reader.java的源码。

public abstract class Reader implements Readable, Closeable {

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

    /**
     * 构造函数之一
     * 创建一个新的字符流Reader,其重要部分将同步其自身。
     */
    protected Reader() {
        this.lock = this;
    }

    /**
     * 构造函数之一
     * 创建一个新的字符流reader,其重要部分将同步给定的对象lock
     */
    protected Reader(Object lock) {
        if (lock == null) {
            throw new NullPointerException();
        }
        this.lock = lock;
    }

    /**
     * 试图将字符读入指定的字符缓冲区。
     * 缓冲区可照原样用作字符的存储库:所做的唯一改变是put操作的结果。不对缓冲区执行翻转或重绕操作。
     * 将输入流管道中的target.length个字节读取到指定的缓存字符数组target中、返回实际存放到target中的字符数。 
     */
    public int read(java.nio.CharBuffer target) throws IOException {
        //获取此缓冲区中的剩余元素数
        int len = target.remaining();
        char[] cbuf = new char[len];
        //试图将输入流中的len个字符读入cbuf,返回实际读取的字节数n
        int n = read(cbuf, 0, len);
        //如果实际读到字符的个数大于0
        if (n > 0)
            //将实际读取到的n个字符存入缓冲区
            target.put(cbuf, 0, n);
        //返回实际读取的字符数
        return n;
    }

    /**
     * 从输入流中读取单个字符。
     */
    public int read() throws IOException {
        char cb[] = new char[1];
        if (read(cb, 0, 1) == -1)
            return -1;
        else
            return cb[0];
    }

    /**
     * 从输入流中读取一定数量的字符并把它们保存到缓冲数组b中。返回读取的字节数。
     */
    public int read(char cbuf[]) throws IOException {
        return read(cbuf, 0, cbuf.length);
    }

    /**
     * 从输入流中,读取最多len个字节到字符数组中。
     *
     * @param   cbuf  存储读入数据的字节数组。
     * @param   off   起始偏移量
     * @param   len   读取的最大字节数。
     * @return  读入字节数组的总字节数,如果由于已到达流末尾而不再有数据,则返回-1。
     */
    abstract public int read(char cbuf[], int off, int len) throws IOException;

    /** 存放被跳过字节的缓冲区的最大值 */
    private static final int maxSkipBufferSize = 8192;

    /** 存放被跳过字节的缓冲区 */
    private char skipBuffer[] = null;

    /**
     * 跳过字符
     *
     * @param  n  要跳过的字符数
     * @return    实际跳过的字符数
     *
     */
    public long skip(long n) throws IOException {
        //如果n为负数,抛出异常
        if (n < 0L)
            throw new IllegalArgumentException("skip value is negative");
        //不允许n大于maxSkipBufferSize
        int nn = (int) Math.min(n, maxSkipBufferSize);
        synchronized (lock) {
            //如果现有缓冲区为null或者大小小于nn,就新建一个大小为nn的缓冲区
            if ((skipBuffer == null) || (skipBuffer.length < nn))
                skipBuffer = new char[nn];
            long r = n;
            //循环跳过输入流中字符,一次尝试跳过nn个字符,直到到达输入流末尾或者n个字符被全部跳过
            while (r > 0) {
                int nc = read(skipBuffer, 0, (int)Math.min(r, nn));
                if (nc == -1)
                    break;
                r -= nc;
            }
            //返回实际跳过的字符数
            return n - r;
        }
    }

    /**
     * 检测此流是否可读
     * 默认返回false
     */
    public boolean ready() throws IOException {
        return false;
    }

    /**
     * 判断此流是否支持mark()操作。默认实现始终返回 false。
     */
    public boolean markSupported() {
        return false;
    }

    /**
     * 标记流中的当前位置。与reset()配合使用。reset()的后续调用将尝试将该流重新定位到此点。
     * 该方法需要子类重写
     */
    public void mark(int readAheadLimit) throws IOException {
        throw new IOException("mark() not supported");
    }

    /**
     * 将流的读取位置重置到最后一次调用mark方法时的位置
     * 该方法需要子类重写
     */
    public void reset() throws IOException {
        throw new IOException("reset() not supported");
    }

    /**
     * 关闭该流并释放与之关联的所有资源。
     */
     abstract public void close() throws IOException;

}

Writer

Writer是字符输出流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。

下面学习下Writer的源码。

public abstract class Writer implements Appendable, Closeable, Flushable {

    /**
     * 字符缓存数组。
     * 用于临时存放要写入字符输出流中的字符
     */
    private char[] writeBuffer;

    /**
     * 字符缓存数组的默认大小。
     */
    private static final int WRITE_BUFFER_SIZE = 1024;

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

    /**
     * 构造方法
     * 创建一个新的字符流writer,其关键部分将同步其自身。
     */
    protected Writer() {
        this.lock = this;
    }

    /**
     * 构造方法
     * 建一个新的字符流writer,其关键部分将同步给定的对象。
     */
    protected Writer(Object lock) {
        if (lock == null) {
            throw new NullPointerException();
        }
        this.lock = lock;
    }

    /**
     * 写入单个字符。
     * 要写入的字符包含在给定整数值的16个低位中,16高位被忽略。
     */
    public void write(int c) throws IOException {
        synchronized (lock) {
            if (writeBuffer == null){
                writeBuffer = new char[WRITE_BUFFER_SIZE];
            }
            //char为16位。所以要写入的字符包含在给定整数值的16个低位中,16高位被忽略。
            writeBuffer[0] = (char) c;
            write(writeBuffer, 0, 1);
        }
    }

    /**
     * 将一个字符数组写入到writerBuffer中 
     */
    public void write(char cbuf[]) throws IOException {
        write(cbuf, 0, cbuf.length);
    }

    /**
     * 试图将字符数组中从off开始的len个字符写入输出流中。
     * 尽量写入len个字符,但写入的字节数可能少于len个,也可能为零。
     */
    abstract public void write(char cbuf[], int off, int len) throws IOException;

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

    /**
     * 试图将字符串中从off开始的len个字符写入输出流中。
     * 尽量写入len个字符,但写入的字节数可能少于len个,也可能为零。
     *
     * @param  试图写入的字符串
     * @param  off 偏移量
     * @param  试图写入的长度
     * @throws  IndexOutOfBoundsException  如果off或len为负,或者off+len 为负或大于给定字符串的长度
     */
    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];
            }
            //将字符串中从off开始到off+len的字符复制到cbuf中
            str.getChars(off, (off + len), cbuf, 0);
            write(cbuf, 0, len);
        }
    }

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

    /**
     * 添加字符序列的一部分
     */
    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;
    }

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

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

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

}

总结

  • Reader是字符输入流的抽象类。子类必须实现的方法只有read(char[], int, int) 和close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。如重写read方法提供更高的效率;重写mark/set方法提供标记功能。
  • Writer是字符输出流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。
Reader与InputStream区别
  • 操作对象的不同。字节流操作字节、字符操作字符。
  • 实现的接口不同。Reader比InputStream多实现了一个Readable接口,用于提供一个可以将字符写入到指定缓存数组的方法。
  • close方法不同。Reader的close方法是抽象的、子类必须重写。 InputStream的close方法则不是抽象的。
Writer与OutputStream区别
  • 操作对象的不同。字节流操作字节、字符操作字符。
  • 实现的接口不同。Writer相比与OutputStream多实现了一个Appendable接口、用于提供几个向此流中追加字符的方法。
  • close、flush方法不同。Writer的close、flush方法都是抽象的,而OutputStream则不是。

关于Reader与Writer就讲到这里,想了解更多内容请参考

版权声明
作者:潘威威

原文地址:CSDN博客-潘威威的博客-http://blog.csdn.net/panweiwei1994/article/details/78312152

本文版权归作者所有,欢迎转载。转载时请在文章明显位置给出原文作者名字(潘威威)及原文链接。请勿将本文用于任何商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值