Java IO流源码学习之一(File字符流与字节流)

本文探讨了Java中的IO流,包括字节流(InputStream, OutputStream)和字符流(Reader, Writer)的顶层抽象类。通过分析FileInputStream和FileOutputStream的源码,揭示了它们如何通过native方法处理文件操作。同时,文章解释了Reader和Writer如何通过InputStreamReader和OutputStreamWriter实现字节流与字符流之间的转换,特别强调了StreamDecoder和StreamEncoder在这一过程中的作用。" 96972200,5731741,Elasticsearch:分布式搜索分析系统详解,"['Elasticsearch', 'Logstash', 'Kibana', '搜索分析', '分布式架构']

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

Java IO流有字节流和字符流两种,其顶层抽象类分别为:
字节流:InputStream与OutputStream两个抽象类
字符流:Reader与Writer两个抽象类
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
以上的图都是来自https://blog.youkuaiyun.com/sinat_37064286/article/details/86537354博客。我只是想记录一下自己的学习情况,大家想了解IO流可以移步至以上博客。
下面是顶层抽象类的实现底层源代码:

InputStream

InputStream 抽象类底层代码:

public abstract class InputStream implements Closeable {

    //用于设置可跳过的最大字节
    private static final int MAX_SKIP_BUFFER_SIZE = 2048;
     //用于读取输入流的一个字节,若未读到任何值,则返回-1
    public abstract int read() throws IOException;

     //将读取的字节存入到相应的数组
    public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
    }
	//读取字节至相应的数组具体实现
    public int read(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

        int c = read();
        if (c == -1) {
            return -1;
        }
        b[off] = (byte)c;

        int i = 1;
        try {
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        return i;
    }
	//跳跃n个字节的具体实现
    public long skip(long n) throws IOException {

        long remaining = n;
        int nr;

        if (n <= 0) {
            return 0;
        }

        int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
        byte[] skipBuffer = new byte[size];
        while (remaining > 0) {
            nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
            if (nr < 0) {
                break;
            }
            remaining -= nr;
        }

        return n - remaining;
    }
	//下面几个方法看具体的实现类
    public int available() throws IOException {
        return 0;
    }

    public void close() throws IOException {}
    
    public synchronized void mark(int readlimit) {}

    public synchronized void reset() throws IOException {
        throw new IOException("mark/reset not supported");
    }

    public boolean markSupported() {
        return false;
    }

}

FileInputStream实现类源码:

public class FileInputStream extends InputStream {
    /**
     *文件描述符类的实例充当基础机器特定结构的不透明句柄,
     *这些特定于机器的结构表示打开的文件,打开的套接字或其他字节源或宿。
     *文件描述符的主要实际用途是创建一个FileInputStream或FileOutputStream来包含它。
	 */
    private final FileDescriptor fd;

    /**
     * The path of the referenced file
     * (null if the stream is created with a file descriptor)
     */
    private final String path;

    private FileChannel channel = null;

    private final Object closeLock = new Object();
    private volatile boolean closed = false;

    public FileInputStream(String name) throws FileNotFoundException {
        this(name != null ? new File(name) : null);
    }
	//构造函数,传入的是File对象
    public FileInputStream(File file) throws FileNotFoundException {
        String name = (file != null ? file.getPath() : null);
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkRead(name);
        }
        if (name == null) {
            throw new NullPointerException();
        }
        if (file.isInvalid()) {
            throw new FileNotFoundException("Invalid file path");
        }
        fd = new FileDescriptor();
        fd.attach(this);
        path = name;
        //用于打开文件,底层调用native方法open0()
        open(name);
    }
	//通过FileDescriptor对象类创建一个FileInputStream对象
    public FileInputStream(FileDescriptor fdObj) {
        SecurityManager security = System.getSecurityManager();
        if (fdObj == null) {
            throw new NullPointerException();
        }
        if (security != null) {
            security.checkRead(fdObj);
        }
        fd = fdObj;
        path = null;

        /*
         * FileDescriptor is being shared by streams.
         * Register this stream with FileDescriptor tracker.
         */
        fd.attach(this);
    }
	//底层用于打开文件
    private native void open0(String name) throws FileNotFoundException;

    // wrap native call to allow instrumentation
    /**
     * Opens the specified file for reading.
     * @param name the name of the file
     */
    private void open(String name) throws FileNotFoundException {
        open0(name);
    }
	//在某些输入可用之前,该方法将阻塞。
    public int read() throws IOException {
        return read0();
    }
	//底层用于读文件
    private native int read0() throws IOException;
	
    private native int readBytes(byte b[], int off, int len) throws IOException;
	//在某些输入可用之前,该方法将阻塞。
    public int read(byte b[]) throws IOException {
        return readBytes(b, 0, b.length);
    }
	//在某些输入可用之前,该方法将阻塞。
    public int read(byte b[], int off, int len) throws IOException {
        return readBytes(b, off, len);
    }
	//skip方法也是调用native方法实现的
    public long skip(long n) throws IOException {
        return skip0(n);
    }

    private native long skip0(long n) throws IOException;
	//返回可以从此输入流读取(或跳过)而不会被该输入流的方法的下一次调用阻塞的剩余字节数的估计值。
    public int available() throws IOException {
        return available0();
    }

    private native int available0() throws IOException;
	//用于关闭该输入流,释放系统资源
    public void close() throws IOException {
        synchronized (closeLock) {
            if (closed) {
                return;
            }
            closed = true;
        }
        if (channel != null) {
           channel.close();
        }

        fd.closeAll(new Closeable() {
            public void close() throws IOException {
               close0();
           }
        });
    }

    //返回FileDescriptor对象,该对象表示与此FileInputStream使用的文件系统中实际文件的连接。
    public final FileDescriptor getFD() throws IOException {
        if (fd != null) {
            return fd;
        }
        throw new IOException();
    }

    //返回与此文件输入流关联的唯一java.nio.channels.FileChannel FileChannel对象。
    public FileChannel getChannel() {
        synchronized (this) {
            if (channel == null) {
                channel = FileChannelImpl.open(fd, path, true, false, this);
            }
            return channel;
        }
    }

    private static native void initIDs();

    private native void close0() throws IOException;

    static {
        initIDs();
    }
	//确保在对象回收时,调用此文件输入流的close方法。
    protected void finalize() throws IOException {
        if ((fd != null) &&  (fd != FileDescriptor.in)) {
            /* if fd is shared, the references in FileDescriptor
             * will ensure that finalizer is only called when
             * safe to do so. All references using the fd have
             * become unreachable. We can call close()
             */
            close();
        }
    }
}

可以看到对文件的具体操作都是通过底层native来实现的,而java只需要调用这些API来对文件进行相应的操作。
FileOutputStream实现源码很多操作都和FileInputStream相似,下面来具体看看FileOutputStream的源码

OutputStream

OutputStream抽象类底层代码:

public abstract class OutputStream implements Closeable, Flushable {
    //写入某个具体字节,字节为8位,因此该int数据的高24位自动忽略,只写入低8位
    public abstract void write(int b) throws IOException;

    //写入一个字节数组
    public void write(byte b[]) throws IOException {
        write(b, 0, b.length);
    }

    public void write(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if ((off < 0) || (off > b.length) || (len < 0) ||
                   ((off + len) > b.length) || ((off + len) < 0)) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return;
        }
        for (int i = 0 ; i < len ; i++) {
            write(b[off + i]);
        }
    }

    //刷新此输出流并强制写出所有缓冲的输出字节。
    public void flush() throws IOException {
    }

    public void close() throws IOException {
    }

}

FileOutputStream实现类源码:

public class FileOutputStream extends OutputStream {
    /**
     * The system dependent file descriptor.
     */
    private final FileDescriptor fd;

    /**
     * True if the file is opened for append.
     */
    private final boolean append;

    /**
     * The associated channel, initialized lazily.
     */
    private FileChannel channel;

    /**
     * The path of the referenced file
     * (null if the stream is created with a file descriptor)
     */
    private final String path;

    private final Object closeLock = new Object();
    private volatile boolean closed = false;

    public FileOutputStream(String name) throws FileNotFoundException {
        this(name != null ? new File(name) : null, false);
    }
	//参数用于控制写入操作的行为,如果append为true的话,写入文件是在具体文件后面添加,而为false的话,则会覆盖原文件
    public FileOutputStream(String name, boolean append)
        throws FileNotFoundException
    {
        this(name != null ? new File(name) : null, append);
    }

    public FileOutputStream(File file) throws FileNotFoundException {
        this(file, false);
    }
	//以上所有的构造方法最终都是调用该构造方法
    public FileOutputStream(File file, boolean append)
        throws FileNotFoundException
    {
        String name = (file != null ? file.getPath() : null);
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkWrite(name);
        }
        if (name == null) {
            throw new NullPointerException();
        }
        if (file.isInvalid()) {
            throw new FileNotFoundException("Invalid file path");
        }
        this.fd = new FileDescriptor();
        fd.attach(this);
        this.append = append;
        this.path = name;

        open(name, append);
    }

    public FileOutputStream(FileDescriptor fdObj) {
        SecurityManager security = System.getSecurityManager();
        if (fdObj == null) {
            throw new NullPointerException();
        }
        if (security != null) {
            security.checkWrite(fdObj);
        }
        this.fd = fdObj;
        this.append = false;
        this.path = null;

        fd.attach(this);
    }

    private native void open0(String name, boolean append)
        throws FileNotFoundException;

    private void open(String name, boolean append)
        throws FileNotFoundException {
        open0(name, append);
    }

    private native void write(int b, boolean append) throws IOException;

    public void write(int b) throws IOException {
        write(b, append);
    }

    private native void writeBytes(byte b[], int off, int len, boolean append)
        throws IOException;

    public void write(byte b[]) throws IOException {
        writeBytes(b, 0, b.length, append);
    }

    public void write(byte b[], int off, int len) throws IOException {
        writeBytes(b, off, len, append);
    }

    public void close() throws IOException {
        synchronized (closeLock) {
            if (closed) {
                return;
            }
            closed = true;
        }

        if (channel != null) {
            channel.close();
        }

        fd.closeAll(new Closeable() {
            public void close() throws IOException {
               close0();
           }
        });
    }

     public final FileDescriptor getFD()  throws IOException {
        if (fd != null) {
            return fd;
        }
        throw new IOException();
     }

    public FileChannel getChannel() {
        synchronized (this) {
            if (channel == null) {
                channel = FileChannelImpl.open(fd, path, false, true, append, this);
            }
            return channel;
        }
    }

    protected void finalize() throws IOException {
        if (fd != null) {
            if (fd == FileDescriptor.out || fd == FileDescriptor.err) {
                flush();
            } else {
                /* if fd is shared, the references in FileDescriptor
                 * will ensure that finalizer is only called when
                 * safe to do so. All references using the fd have
                 * become unreachable. We can call close()
                 */
                close();
            }
        }
    }

    private native void close0() throws IOException;

    private static native void initIDs();

    static {
        initIDs();
    }

}

可以看到FileOutputStream的具体实现与FileInputStream的实现非常相似。
下面再来看看字符流Reader与Writer。

Reader

Reader字符流抽象类源码:

public abstract class Reader implements Readable, Closeable {

    protected Object lock;

    protected Reader() {
        this.lock = this;
    }

    protected Reader(Object lock) {
        if (lock == null) {
            throw new NullPointerException();
        }
        this.lock = lock;
    }
	//将字符读入CharBuffer 
    public int read(java.nio.CharBuffer target) throws IOException {
        int len = target.remaining();
        char[] cbuf = new char[len];
        int n = read(cbuf, 0, len);
        if (n > 0)
            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];
    }
	//将字符读入数组
    public int read(char cbuf[]) throws IOException {
        return read(cbuf, 0, cbuf.length);
    }

    abstract public int read(char cbuf[], int off, int len) throws IOException;

    /** Maximum skip-buffer size */
    private static final int maxSkipBufferSize = 8192;

    /** Skip buffer, null until allocated */
    private char skipBuffer[] = null;

    /**
     * Skips characters.  This method will block until some characters are
     * available, an I/O error occurs, or the end of the stream is reached.
     *
     * @param  n  The number of characters to skip
     *
     * @return    The number of characters actually skipped
     *
     * @exception  IllegalArgumentException  If <code>n</code> is negative.
     * @exception  IOException  If an I/O error occurs
     */
    public long skip(long n) throws IOException {
        if (n < 0L)
            throw new IllegalArgumentException("skip value is negative");
        int nn = (int) Math.min(n, maxSkipBufferSize);
        synchronized (lock) {
            if ((skipBuffer == null) || (skipBuffer.length < nn))
                skipBuffer = new char[nn];
            long r = n;
            while (r > 0) {
                int nc = read(skipBuffer, 0, (int)Math.min(r, nn));
                if (nc == -1)
                    break;
                r -= nc;
            }
            return n - r;
        }
    }
	//下面看具体实现类
    public boolean ready() throws IOException {
        return false;
    }

    public boolean markSupported() {
        return false;
    }

    public void mark(int readAheadLimit) throws IOException {
        throw new IOException("mark() not supported");
    }

    public void reset() throws IOException {
        throw new IOException("reset() not supported");
    }

     abstract public void close() throws IOException;

}

FileReader实现源码:

public class FileReader extends InputStreamReader {

    public FileReader(String fileName) throws FileNotFoundException {
        super(new FileInputStream(fileName));
    }

    public FileReader(File file) throws FileNotFoundException {
        super(new FileInputStream(file));
    }

    public FileReader(FileDescriptor fd) {
        super(new FileInputStream(fd));
    }

}

FileReader源码中只有三个构造方法,并且三个构造方法都是调用的其父类InputStreamReader 的构造方法,并且传入的参数正好是字节流的FileInputStream对象,通过对象InputStreamReader 将字节流转换为字符流。
InputStreamReader 的源码如下:

public class InputStreamReader extends Reader {

    private final StreamDecoder sd;

    //由于字符流,就必须要考虑编码问题,该构造函数采用默认的编码,得到StreamDecoder对象,实际操作就是使用StreamDecoder进行字符读
    public InputStreamReader(InputStream in) {
        super(in);
        try {
            sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
        } catch (UnsupportedEncodingException e) {
            // The default encoding should always be available
            throw new Error(e);
        }
    }
	//该构造方法指定了编码
    public InputStreamReader(InputStream in, String charsetName)
        throws UnsupportedEncodingException
    {
        super(in);
        if (charsetName == null)
            throw new NullPointerException("charsetName");
        sd = StreamDecoder.forInputStreamReader(in, this, charsetName);
    }
	//该构造方法也指定了具体的编码,但是编码是由Charset对象给出
    public InputStreamReader(InputStream in, Charset cs) {
        super(in);
        if (cs == null)
            throw new NullPointerException("charset");
        sd = StreamDecoder.forInputStreamReader(in, this, cs);
    }

    public InputStreamReader(InputStream in, CharsetDecoder dec) {
        super(in);
        if (dec == null)
            throw new NullPointerException("charset decoder");
        sd = StreamDecoder.forInputStreamReader(in, this, dec);
    }
	//获取字符编码
    public String getEncoding() {
        return sd.getEncoding();
    }
	//读操作
    public int read() throws IOException {
        return sd.read();
    }
    
    public int read(char cbuf[], int offset, int length) throws IOException {
        return sd.read(cbuf, offset, length);
    }

    //告诉此流是否已准备好被读取。 如果InputStreamReader的输入缓冲区不为空,或者可以从基础字节流中读取字节,则它准备就绪。
    public boolean ready() throws IOException {
        return sd.ready();
    }

    public void close() throws IOException {
        sd.close();
    }
}

通过StreamDecoder.forInputStreamReader()方法,实现字节流与字符流的转换,然后返回一个StreamDecoder对象,对字符流的读操作都是通过StreamDecoder进行操作的。下面来看StreamDecoder类的具体实现。

public class StreamDecoder extends Reader {

	//定义最小以及默认的ByteBuffer的大小
    private static final int MIN_BYTE_BUFFER_SIZE = 32;
    private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192;
	// 确保流是被打开状态否则不能输入输出
    private volatile boolean isOpen = true;

    private void ensureOpen() throws IOException {
        if (!isOpen)
            throw new IOException("Stream closed");
    }

    //read方法要求返回一个字符,但是实际是读取2个字符,剩余1个字符会被赋予给leftoverChar变量,而haveLeftoverChar用于判断
    private boolean haveLeftoverChar = false;
    private char leftoverChar;


    // 以下的forInputStreamReader方法最后都是调用返回StreamDecoder 的构造方法

    public static StreamDecoder forInputStreamReader(InputStream in,
                                                     Object lock,
                                                     String charsetName)
        throws UnsupportedEncodingException
    {
        String csn = charsetName;
        if (csn == null)
            csn = Charset.defaultCharset().name();
        try {
            if (Charset.isSupported(csn))
                return new StreamDecoder(in, lock, Charset.forName(csn));
        } catch (IllegalCharsetNameException x) { }
        throw new UnsupportedEncodingException (csn);
    }

    public static StreamDecoder forInputStreamReader(InputStream in,
                                                     Object lock,
                                                     Charset cs)
    {
        return new StreamDecoder(in, lock, cs);
    }

    public static StreamDecoder forInputStreamReader(InputStream in,
                                                     Object lock,
                                                     CharsetDecoder dec)
    {
        return new StreamDecoder(in, lock, dec);
    }


    // Factory for java.nio.channels.Channels.newReader

    public static StreamDecoder forDecoder(ReadableByteChannel ch,
                                           CharsetDecoder dec,
                                           int minBufferCap)
    {
        return new StreamDecoder(ch, dec, minBufferCap);
    }
	//返回字符编码
    public String getEncoding() {
        if (isOpen())
            return encodingName();
        return null;
    }

    public int read() throws IOException {
        return read0();
    }
	//用于读取一个字符,实际读取了两个字符
    @SuppressWarnings("fallthrough")
    private int read0() throws IOException {
        synchronized (lock) {

            // Return the leftover char, if there is one
            if (haveLeftoverChar) {
                haveLeftoverChar = false;
                return leftoverChar;
            }

            // Convert more bytes
            char cb[] = new char[2];
            int n = read(cb, 0, 2);
            switch (n) {
            case -1:
                return -1;
            case 2:
                leftoverChar = cb[1];
                haveLeftoverChar = true;
                // FALL THROUGH
            case 1:
                return cb[0];
            default:
                assert false : n;
                return -1;
            }
        }
    }
	//读取长度为length的字符至cbuf,其会调用implRead方法
    public int read(char cbuf[], int offset, int length) throws IOException {
        int off = offset;
        int len = length;
        synchronized (lock) {
            ensureOpen();
            if ((off < 0) || (off > cbuf.length) || (len < 0) ||
                ((off + len) > cbuf.length) || ((off + len) < 0)) {
                throw new IndexOutOfBoundsException();
            }
            if (len == 0)
                return 0;

            int n = 0;

            if (haveLeftoverChar) {
                // Copy the leftover char into the buffer
                cbuf[off] = leftoverChar;
                off++; len--;
                haveLeftoverChar = false;
                n = 1;
                if ((len == 0) || !implReady())
                    // Return now if this is all we can produce w/o blocking
                    return n;
            }

            if (len == 1) {
                // Treat single-character array reads just like read()
                int c = read0();
                if (c == -1)
                    return (n == 0) ? -1 : n;
                cbuf[off] = (char)c;
                return n + 1;
            }

            return n + implRead(cbuf, off, off + len);
        }
    }
	
    public boolean ready() throws IOException {
        synchronized (lock) {
            ensureOpen();
            return haveLeftoverChar || implReady();
        }
    }

    public void close() throws IOException {
        synchronized (lock) {
            if (!isOpen)
                return;
            implClose();
            isOpen = false;
        }
    }

    private boolean isOpen() {
        return isOpen;
    }


    // -- Charset-based stream decoder impl --

    // In the early stages of the build we haven't yet built the NIO native
    // code, so guard against that by catching the first UnsatisfiedLinkError
    // and setting this flag so that later attempts fail quickly.
    //
    private static volatile boolean channelsAvailable = true;

    private static FileChannel getChannel(FileInputStream in) {
        if (!channelsAvailable)
            return null;
        try {
            return in.getChannel();
        } catch (UnsatisfiedLinkError x) {
            channelsAvailable = false;
            return null;
        }
    }

    private Charset cs;
    private CharsetDecoder decoder;
    private ByteBuffer bb;

    // Exactly one of these is non-null
    private InputStream in;
    private ReadableByteChannel ch;

    StreamDecoder(InputStream in, Object lock, Charset cs) {
        this(in, lock,
         cs.newDecoder()
         .onMalformedInput(CodingErrorAction.REPLACE)
         .onUnmappableCharacter(CodingErrorAction.REPLACE));
    }

    StreamDecoder(InputStream in, Object lock, CharsetDecoder dec) {
        super(lock);
        this.cs = dec.charset();
        this.decoder = dec;

        // This path disabled until direct buffers are faster
        if (false && in instanceof FileInputStream) {
        ch = getChannel((FileInputStream)in);
        if (ch != null)
            bb = ByteBuffer.allocateDirect(DEFAULT_BYTE_BUFFER_SIZE);
        }
        if (ch == null) {
        this.in = in;
        this.ch = null;
        bb = ByteBuffer.allocate(DEFAULT_BYTE_BUFFER_SIZE);
        }
        bb.flip();                      // So that bb is initially empty
    }

    StreamDecoder(ReadableByteChannel ch, CharsetDecoder dec, int mbc) {
        this.in = null;
        this.ch = ch;
        this.decoder = dec;
        this.cs = dec.charset();
        this.bb = ByteBuffer.allocate(mbc < 0
                                  ? DEFAULT_BYTE_BUFFER_SIZE
                                  : (mbc < MIN_BYTE_BUFFER_SIZE
                                     ? MIN_BYTE_BUFFER_SIZE
                                     : mbc));
        bb.flip();
    }

    private int readBytes() throws IOException {
        bb.compact();
        try {
        if (ch != null) {
            // Read from the channel
            int n = ch.read(bb);
            if (n < 0)
                return n;
        } else {
            // Read from the input stream, and then update the buffer
            int lim = bb.limit();
            int pos = bb.position();
            assert (pos <= lim);
            int rem = (pos <= lim ? lim - pos : 0);
            assert rem > 0;
            int n = in.read(bb.array(), bb.arrayOffset() + pos, rem);
            if (n < 0)
                return n;
            if (n == 0)
                throw new IOException("Underlying input stream returned zero bytes");
            assert (n <= rem) : "n = " + n + ", rem = " + rem;
            bb.position(pos + n);
        }
        } finally {
        // Flip even when an IOException is thrown,
        // otherwise the stream will stutter
        bb.flip();
        }

        int rem = bb.remaining();
            assert (rem != 0) : rem;
            return rem;
    }
	//该方法实现将字节缓冲内的字节解码成字符,放入字符缓冲
    int implRead(char[] cbuf, int off, int end) throws IOException {

        // In order to handle surrogate pairs, this method requires that
        // the invoker attempt to read at least two characters.  Saving the
        // extra character, if any, at a higher level is easier than trying
        // to deal with it here.
        assert (end - off > 1);

        CharBuffer cb = CharBuffer.wrap(cbuf, off, end - off);
        if (cb.position() != 0)
        // Ensure that cb[0] == cbuf[off]
        cb = cb.slice();

        boolean eof = false;
        for (;;) {
        //具体解码
        CoderResult cr = decoder.decode(bb, cb, eof);
        // 代表字节缓冲区内容已全部解码放入到字符缓冲区中
        if (cr.isUnderflow()) {
            if (eof)
                break;
            if (!cb.hasRemaining())
                break;
            if ((cb.position() > 0) && !inReady())
                break;          // Block at most once
            //读取字节到字节缓冲区bb中
            int n = readBytes();
            if (n < 0) {
                eof = true;
                if ((cb.position() == 0) && (!bb.hasRemaining()))
                    break;
                decoder.reset();
            }
            continue;
        }
        //代表着字符缓冲区已满,应该使用未满的字符缓冲区在次调用该方法
        if (cr.isOverflow()) {
            assert cb.position() > 0;
            break;
        }
        cr.throwException();
        }

        if (eof) {
        // ## Need to flush decoder
        decoder.reset();
        }

        if (cb.position() == 0) {
            if (eof)
                return -1;
            assert false;
        }
        return cb.position();
    }

    String encodingName() {
        return ((cs instanceof HistoricallyNamedCharset)
            ? ((HistoricallyNamedCharset)cs).historicalName()
            : cs.name());
    }

    private boolean inReady() {
        try {
        return (((in != null) && (in.available() > 0))
                || (ch instanceof FileChannel)); // ## RBC.available()?
        } catch (IOException x) {
        return false;
        }
    }

    boolean implReady() {
            return bb.hasRemaining() || inReady();
    }

    void implClose() throws IOException {
        if (ch != null)
        ch.close();
        else
        in.close();
    }

}

Writer

Writer字符流抽象类源码:

public abstract class Writer implements Appendable, Closeable, Flushable {

    /**
     * Temporary buffer used to hold writes of strings and single characters
     */
    private char[] writeBuffer;

    /**
     * Size of writeBuffer, must be >= 1
     */
    private static final int WRITE_BUFFER_SIZE = 1024;

    protected Object lock;

    protected Writer() {
        this.lock = this;
    }

    protected Writer(Object lock) {
        if (lock == null) {
            throw new NullPointerException();
        }
        this.lock = lock;
    }
	//具体写操作,写的时候上锁
    public void write(int c) throws IOException {
        synchronized (lock) {
            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;
    }

    public Writer append(char c) throws IOException {
        write(c);
        return this;
    }

    abstract public void flush() throws IOException;

    abstract public void close() throws IOException;

}

FileWriter源码如下:

public class FileWriter extends OutputStreamWriter {

    public FileWriter(String fileName) throws IOException {
        super(new FileOutputStream(fileName));
    }

    public FileWriter(String fileName, boolean append) throws IOException {
        super(new FileOutputStream(fileName, append));
    }

    public FileWriter(File file) throws IOException {
        super(new FileOutputStream(file));
    }

    public FileWriter(File file, boolean append) throws IOException {
        super(new FileOutputStream(file, append));
    }

    public FileWriter(FileDescriptor fd) {
        super(new FileOutputStream(fd));
    }

}

与FileReader相似,也是通过继承OutputStreamWriter来实现的,采用适配器模式:
下面是OutputStreamWriter实现源码,与InputStreamReader也很相似

public class OutputStreamWriter extends Writer {

    private final StreamEncoder se;

    public OutputStreamWriter(OutputStream out, String charsetName)
        throws UnsupportedEncodingException
    {
        super(out);
        if (charsetName == null)
            throw new NullPointerException("charsetName");
        se = StreamEncoder.forOutputStreamWriter(out, this, charsetName);
    }

    public OutputStreamWriter(OutputStream out) {
        super(out);
        try {
            se = StreamEncoder.forOutputStreamWriter(out, this, (String)null);
        } catch (UnsupportedEncodingException e) {
            throw new Error(e);
        }
    }

    public OutputStreamWriter(OutputStream out, Charset cs) {
        super(out);
        if (cs == null)
            throw new NullPointerException("charset");
        se = StreamEncoder.forOutputStreamWriter(out, this, cs);
    }

    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();
    }

    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();
    }
}

最后也是通过StreamEncoder对象,将字符流编码成字节流。
由上面可知,字符流实际上就是通过字节流解码而来的,底层通过FileInputStream读取字节流,有点适配器模式的味道。
关于IO流的其它类源码以后再看吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值