I/O流-Reader类

273 阅读8分钟

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

基本概念

Reader是一个抽象类,它是以字符为单位的输入流的公共父类。

构造函数及方法

protected Reader() //创建一个新的字符流 reader,其重要部分将同步其自身的 reader。
protected Reader(Object lock) //创建一个新的字符流 reader,其重要部分将同步给定的对象。
abstract  void  close() //关闭该流并释放与之关联的所有资源。
void  mark(int readAheadLimit) //标记流中的当前位置。
boolean  markSupported() //判断此流是否支持 mark() 操作。
int  read() //读取单个字符。
int  read(char[] cbuf) //将字符读入数组。
abstract  int  read(char[] cbuf, int off, int len) //将字符读入数组的某一部分。
int  read(CharBuffer target) //试图将字符读入指定的字符缓冲区。
boolean  ready() //判断是否准备读取此流。
void  reset() //重置该流。
long  skip(long n) //跳过字符。

Reader类图

CharArrayReader是字符数组输入流,用于读取字符数组。

PipedReader 是字符管道输入流,它继承于Writer。

FileReader 方便读取字符文件的。

BufferedReader 从流里面读取文本,通过缓存的方式提高效率,读取的内容包括字符、数组和行。缓存的大小可以指定,也可以用默认的大小,大部分情况下,默认大小就够了。

InputStreamReader 把字节翻译成字符的,可以处理乱码问题。

CharArrayReader类

CharArrayReader是字符数组输入流,用于读取字符数组,继承于Reader操作的数据是以字符为单位。

CharArrayReader实际上是通过“字符数组”去保存数据。

源码分析

public class CharArrayReader extends Reader {
    // 字符数组缓冲
    protected char buf[];

    // 下一个被获取的字符的位置
    protected int pos;

    // 被标记的位置
    protected int markedPos = 0;

    // 字符缓冲的长度
    protected int count;

    // 构造函数
    public CharArrayReader(char buf[]) {
        this.buf = buf;
        this.pos = 0;
        this.count = buf.length;
    }

    // 构造函数
    public CharArrayReader(char buf[], int offset, int length) {
        if ((offset < 0) || (offset > buf.length) || (length < 0) ||
            ((offset + length) < 0)) {
            throw new IllegalArgumentException();
        }
        this.buf = buf;
        this.pos = offset;
        this.count = Math.min(offset + length, buf.length);
        this.markedPos = offset;
    }

    // 判断“CharArrayReader是否有效”。
    // 若字符缓冲为null,则认为其无效。
    private void ensureOpen() throws IOException {
        if (buf == null)
            throw new IOException("Stream closed");
    }

    // 读取下一个字符。即返回字符缓冲区中下一位置的值。
    // 注意:读取的是字符!
    public int read() throws IOException {
        synchronized (lock) {
            ensureOpen();
            if (pos >= count)
                return -1;
            else
                return buf[pos++];
        }
    }

    // 读取数据,并保存到字符数组b中从off开始的位置中,len是读取长度。
    public int read(char b[], int off, int len) throws IOException {
        synchronized (lock) {
            ensureOpen();
            if ((off < 0) || (off > b.length) || (len < 0) ||
                ((off + len) > b.length) || ((off + len) < 0)) {
                throw new IndexOutOfBoundsException();
            } else if (len == 0) {
                return 0;
            }

            if (pos >= count) {
                return -1;
            }
            if (pos + len > count) {
                len = count - pos;
            }
            if (len <= 0) {
                return 0;
            }
            System.arraycopy(buf, pos, b, off, len);
            pos += len;
            return len;
        }
    }

    // 跳过n个字符
    public long skip(long n) throws IOException {
        synchronized (lock) {
            ensureOpen();
            if (pos + n > count) {
                n = count - pos;
            }
            if (n < 0) {
                return 0;
            }
            pos += n;
            return n;
        }
    }

    // 判断“是否能读取下一个字符”。能的话,返回true。
    public boolean ready() throws IOException {
        synchronized (lock) {
            ensureOpen();
            return (count - pos) > 0;
        }
    }

    public boolean markSupported() {
        return true;
    }

    // 保存当前位置。readAheadLimit在此处没有任何实际意义
    // mark()必须和reset()配合使用才有意义!
    public void mark(int readAheadLimit) throws IOException {
        synchronized (lock) {
            ensureOpen();
            markedPos = pos;
        }
    }

    // 重置“下一个读取位置”为“mark所标记的位置”
    public void reset() throws IOException {
        synchronized (lock) {
            ensureOpen();
            pos = markedPos;
        }
    }

    public void close() {
        buf = null;
    }
}

PipedReader类

PipedReader 是字符管道输入流,它继承于Writer。它用于读取对应绑定的管道字符输出流写入其内置字符缓存数组buffer中的字符、借此来实现线程之间的通信、PipedReader中专门有两个方法供PipedWriter调用、receive(char c)、receive(char[] b, int off, intlen)、使得PipedWriter可以将字符或者字符数组写入PipedReader的buffer中。

源码分析

public class PipedReader extends Reader {
    // “PipedWriter”是否关闭的标记
    boolean closedByWriter = false;
    // “PipedReader”是否关闭的标记
    boolean closedByReader = false;
    // “PipedReader”与“PipedWriter”是否连接的标记
    // 它在PipedWriter的connect()连接函数中被设置为true
    boolean connected = false;

    Thread readSide;    // 读取“管道”数据的线程
    Thread writeSide;    // 向“管道”写入数据的线程

    // “管道”的默认大小
    private static final int DEFAULT_PIPE_SIZE = 1024;

    // 缓冲区
    char buffer[];

    //下一个写入字符的位置。in==out代表满,说明“写入的数据”全部被读取了。
    int in = -1;
    //下一个读取字符的位置。in==out代表满,说明“写入的数据”全部被读取了。
    int out = 0;

    // 构造函数:指定与“PipedReader”关联的“PipedWriter”
    public PipedReader(PipedWriter src) throws IOException {
        this(src, DEFAULT_PIPE_SIZE);
    }

    // 构造函数:指定与“PipedReader”关联的“PipedWriter”,以及“缓冲区大小”
    public PipedReader(PipedWriter src, int pipeSize) throws IOException {
        initPipe(pipeSize);
        connect(src);
    }

    // 构造函数:默认缓冲区大小是1024字符
    public PipedReader() {
        initPipe(DEFAULT_PIPE_SIZE);
    }

    // 构造函数:指定缓冲区大小是pipeSize
    public PipedReader(int pipeSize) {
        initPipe(pipeSize);
    }

    // 初始化“管道”:新建缓冲区大小
    private void initPipe(int pipeSize) {
        if (pipeSize <= 0) {
            throw new IllegalArgumentException("Pipe size <= 0");
        }
        buffer = new char[pipeSize];
    }

    // 将“PipedReader”和“PipedWriter”绑定。
    // 实际上,这里调用的是PipedWriter的connect()函数
    public void connect(PipedWriter src) throws IOException {
        src.connect(this);
    }

    // 接收int类型的数据b。
    // 它只会在PipedWriter的write(int b)中会被调用
    synchronized void receive(int c) throws IOException {
        // 检查管道状态
        if (!connected) {
            throw new IOException("Pipe not connected");
        } else if (closedByWriter || closedByReader) {
            throw new IOException("Pipe closed");
        } else if (readSide != null && !readSide.isAlive()) {
            throw new IOException("Read end dead");
        }

        // 获取“写入管道”的线程
        writeSide = Thread.currentThread();
        // 如果“管道中被读取的数据,等于写入管道的数据”时,
        // 则每隔1000ms检查“管道状态”,并唤醒管道操作:若有“读取管道数据线程被阻塞”,则唤醒该线程。
        while (in == out) {
            if ((readSide != null) && !readSide.isAlive()) {
                throw new IOException("Pipe broken");
            }
            /* full: kick any waiting readers */
            notifyAll();
            try {
                wait(1000);
            } catch (InterruptedException ex) {
                throw new java.io.InterruptedIOException();
            }
        }
        if (in < 0) {
            in = 0;
            out = 0;
        }
        buffer[in++] = (char) c;
        if (in >= buffer.length) {
            in = 0;
        }
    }

    // 接收字符数组b。
    synchronized void receive(char c[], int off, int len)  throws IOException {
        while (--len >= 0) {
            receive(c[off++]);
        }
    }

    // 当PipedWriter被关闭时,被调用
    synchronized void receivedLast() {
        closedByWriter = true;
        notifyAll();
    }

    // 从管道(的缓冲)中读取一个字符,并将其转换成int类型
    public synchronized int read()  throws IOException {
        if (!connected) {
            throw new IOException("Pipe not connected");
        } else if (closedByReader) {
            throw new IOException("Pipe closed");
        } else if (writeSide != null && !writeSide.isAlive()
                   && !closedByWriter && (in < 0)) {
            throw new IOException("Write end dead");
        }

        readSide = Thread.currentThread();
        int trials = 2;
        while (in < 0) {
            if (closedByWriter) {
                /* closed by writer, return EOF */
                return -1;
            }
            if ((writeSide != null) && (!writeSide.isAlive()) && (--trials < 0)) {
                throw new IOException("Pipe broken");
            }
            /* might be a writer waiting */
            notifyAll();
            try {
                wait(1000);
            } catch (InterruptedException ex) {
                throw new java.io.InterruptedIOException();
            }
        }
        int ret = buffer[out++];
        if (out >= buffer.length) {
            out = 0;
        }
        if (in == out) {
            /* now empty */
            in = -1;
        }
        return ret;
    }

    // 从管道(的缓冲)中读取数据,并将其存入到数组b中
    public synchronized int read(char cbuf[], int off, int len)  throws IOException {
        if (!connected) {
            throw new IOException("Pipe not connected");
        } else if (closedByReader) {
            throw new IOException("Pipe closed");
        } else if (writeSide != null && !writeSide.isAlive()
                   && !closedByWriter && (in < 0)) {
            throw new IOException("Write end dead");
        }

        if ((off < 0) || (off > cbuf.length) || (len < 0) ||
            ((off + len) > cbuf.length) || ((off + len) < 0)) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

        /* possibly wait on the first character */
        int c = read();
        if (c < 0) {
            return -1;
        }
        cbuf[off] =  (char)c;
        int rlen = 1;
        while ((in >= 0) && (--len > 0)) {
            cbuf[off + rlen] = buffer[out++];
            rlen++;
            if (out >= buffer.length) {
                out = 0;
            }
            if (in == out) {
                /* now empty */
                in = -1;
            }
        }
        return rlen;
    }

    // 是否能从管道中读取下一个数据
    public synchronized boolean ready() throws IOException {
        if (!connected) {
            throw new IOException("Pipe not connected");
        } else if (closedByReader) {
            throw new IOException("Pipe closed");
        } else if (writeSide != null && !writeSide.isAlive()
                   && !closedByWriter && (in < 0)) {
            throw new IOException("Write end dead");
        }
        if (in < 0) {
            return false;
        } else {
            return true;
        }
    }

    // 关闭PipedReader
    public void close()  throws IOException {
        in = -1;
        closedByReader = true;
    }
}

FileReader类

FilterReader是实现自定义过滤输入字符流的基类,本事是一个抽象类、为所有装饰类提供一个标准、只是简单重写了父类Reader的所有方法、要求子类必须重写核心方法、和提供具有自己特色的方法、这里没有像字节流那样有很多的子类来实现不同的功能、可能是因为字符流本来就是字节流的一种装饰、所以在这里没有必要再对其进行装饰、只是提供一个扩展的接口而已。

源码分析

/** 
 * 本身是一个抽象类、只简单重写了父类Reader的一些方法、作用是为所有装饰类提供一个标准、 
 * 子类要重写父类的核心方法和扩展具有自己特色的方法、目前没有子类、作用是为以后的扩展提供一个接口 
 */  
public abstract class FilterReader extends Reader {  
    /**底层字符输入流*/  
    protected Reader in;  
  
    /** 使用传入的底层字符输入流创建FilterReader*/  
    protected FilterReader(Reader in) {  
        super(in);  
        this.in = in;  
    }  
  
    /**读取一个字符*/  
    public int read() throws IOException {  
        return in.read();  
    }  
  
    /**将字符读取到字符数组cbuf中*/  
    public int read(char cbuf[], int off, int len) throws IOException {  
        return in.read(cbuf, off, len);  
    }  
  
    /**跳过底层输入流中的n个字符*/  
    public long skip(long n) throws IOException {  
        return in.skip(n);  
    }  
  
    /**检测此流是否可以读取 */  
    public boolean ready() throws IOException {  
        return in.ready();  
    }  
  
    /**检测此流是否支持mark*/  
    public boolean markSupported() {  
        return in.markSupported();  
    }  
  
    /**标记此流*/  
    public void mark(int readAheadLimit) throws IOException {  
        in.mark(readAheadLimit);  
    }  
  
    /**重置最后一次mark的位置 */  
    public void reset() throws IOException {  
        in.reset();  
    }  
    /**关闭此流 */  
    public void close() throws IOException {  
        in.close();  
    }
}

BufferedReader类

BufferedReader能为字符输入流提供缓冲区,可以提高许多IO处理的速度。你可以一次读取一大块的数据,而不需要每次从网络或者磁盘中一次读取一个字节。特别是在访问大量磁盘数据时,缓冲通常会让IO快上许多。

源码分析

public class BufferedReader extends Reader {  
 
    private Reader in;  
 
    // 字符缓冲区  
    private char cb[];  
    // nChars 是cb缓冲区中字符的总的个数  
    // nextChar 是下一个要读取的字符在cb缓冲区中的位置  
    private int nChars, nextChar;  
 
    // 表示“标记无效”。它与UNMARKED的区别是:  
    // (01) UNMARKED 是压根就没有设置过标记。  
    // (02) 而INVALIDATED是设置了标记,但是被标记位置太长,导致标记无效!  
    private static final int INVALIDATED = -2;  
    // 表示没有设置“标记”  
    private static final int UNMARKED = -1;  
    // “标记”  
    private int markedChar = UNMARKED;  
    // “标记”能标记位置的最大长度  
    private int readAheadLimit = 0; /* Valid only when markedChar > 0 */ 
 
    // skipLF(即skip Line Feed)是“是否忽略换行符”标记  
    private boolean skipLF = false;  
 
    // 设置“标记”时,保存的skipLF的值  
    private boolean markedSkipLF = false;  
 
    // 默认字符缓冲区大小  
    private static int defaultCharBufferSize = 8192;  
    // 默认每一行的字符个数  
    private static int defaultExpectedLineLength = 80;  
 
    // 创建“Reader”对应的BufferedReader对象,sz是BufferedReader的缓冲区大小  
    public BufferedReader(Reader in, int sz) {  
        super(in);  
        if (sz <= 0)  
            throw new IllegalArgumentException("Buffer size <= 0");  
        this.in = in;  
        cb = new char[sz];  
        nextChar = nChars = 0;  
    }  
 
    // 创建“Reader”对应的BufferedReader对象,默认的BufferedReader缓冲区大小是8k  
    public BufferedReader(Reader in) {  
        this(in, defaultCharBufferSize);  
    }  
 
    // 确保“BufferedReader”是打开状态  
    private void ensureOpen() throws IOException {  
        if (in == null)  
            throw new IOException("Stream closed");  
    }  
 
    // 填充缓冲区函数。有以下两种情况被调用:  
    // (01) 缓冲区没有数据时,通过fill()可以向缓冲区填充数据。  
    // (02) 缓冲区数据被读完,需更新时,通过fill()可以更新缓冲区的数据。  
    private void fill() throws IOException {  
        // dst表示“cb中填充数据的起始位置”。  
        int dst;  
        if (markedChar <= UNMARKED) {  
            // 没有标记的情况,则设dst=0dst = 0;  
        } else {  
            // delta表示“当前标记的长度”,它等于“下一个被读取字符的位置”减去“标记的位置”的差值;  
            int delta = nextChar - markedChar;  
            if (delta >= readAheadLimit) {  
                // 若“当前标记的长度”超过了“标记上限(readAheadLimit)”,  
                // 则丢弃标记!  
                markedChar = INVALIDATED;  
                readAheadLimit = 0;  
                dst = 0;  
            } else {  
                if (readAheadLimit <= cb.length) {  
                    // 若“当前标记的长度”没有超过了“标记上限(readAheadLimit)”,  
                    // 并且“标记上限(readAheadLimit)”小于/等于“缓冲的长度”;  
                    // 则先将“下一个要被读取的位置,距离我们标记的置符的距离”间的字符保存到cb中。  
                    System.arraycopy(cb, markedChar, cb, 0, delta);  
                    markedChar = 0;  
                    dst = delta;  
                } else {  
                    // 若“当前标记的长度”没有超过了“标记上限(readAheadLimit)”,  
                    // 并且“标记上限(readAheadLimit)”大于“缓冲的长度”;  
                    // 则重新设置缓冲区大小,并将“下一个要被读取的位置,距离我们标记的置符的距离”间的字符保存到cb中。  
                    char ncb[] = new char[readAheadLimit];  
                    System.arraycopy(cb, markedChar, ncb, 0, delta);  
                    cb = ncb;  
                    markedChar = 0;  
                    dst = delta;  
                }  
                // 更新nextChar和nChars  
                nextChar = nChars = delta;  
            }  
        }  
 
        int n;  
        do {  
            // 从“in”中读取数据,并存储到字符数组cb中;  
            // 从cb的dst位置开始存储,读取的字符个数是cb.length - dst  
            // n是实际读取的字符个数;若n==0(即一个也没读到),则继续读取!  
            n = in.read(cb, dst, cb.length - dst);  
        } while (n == 0);  
 
        // 如果从“in”中读到了数据,则设置nChars(cb中字符的数目)=dst+n,  
        // 并且nextChar(下一个被读取的字符的位置)=dst。  
        if (n > 0) {  
            nChars = dst + n;  
            nextChar = dst;  
        }  
    }  
 
    // 从BufferedReader中读取一个字符,该字符以int的方式返回  
    public int read() throws IOException {  
        synchronized (lock) {  
            ensureOpen();  
            for (;;) {  
                // 若“缓冲区的数据已经被读完”,  
                // 则先通过fill()更新缓冲区数据  
                if (nextChar >= nChars) {  
                    fill();  
                    if (nextChar >= nChars)  
                        return -1;  
                }  
                // 若要“忽略换行符”,  
                // 则对下一个字符是否是换行符进行处理。  
                if (skipLF) {  
                    skipLF = false;  
                    if (cb[nextChar] == '\n') {  
                        nextChar++;  
                        continue;  
                    }  
                }  
                // 返回下一个字符  
                return cb[nextChar++];  
            }  
        }  
    }  
 
    // 将缓冲区中的数据写入到数组cbuf中。off是数组cbuf中的写入起始位置,len是写入长度  
    private int read1(char[] cbuf, int off, int len) throws IOException {  
        // 若“缓冲区的数据已经被读完”,则更新缓冲区数据。  
        if (nextChar >= nChars) {  
            if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {  
                return in.read(cbuf, off, len);  
            }  
            fill();  
        }  
        // 若更新数据之后,没有任何变化;则退出。  
        if (nextChar >= nChars) return -1;  
        // 若要“忽略换行符”,则进行相应处理  
        if (skipLF) {  
            skipLF = false;  
            if (cb[nextChar] == '\n') {  
                nextChar++;  
                if (nextChar >= nChars)  
                    fill();  
                if (nextChar >= nChars)  
                    return -1;  
            }  
        }  
        // 拷贝字符操作  
        int n = Math.min(len, nChars - nextChar);  
        System.arraycopy(cb, nextChar, cbuf, off, n);  
        nextChar += n;  
        return n;  
    }  
 
    // 对read1()的封装,添加了“同步处理”和“阻塞式读取”等功能  
    public int read(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 0;  
            }  
 
            int n = read1(cbuf, off, len);  
            if (n <= 0) return n;  
            while ((n < len) && in.ready()) {  
                int n1 = read1(cbuf, off + n, len - n);  
                if (n1 <= 0) break;  
                n += n1;  
            }  
            return n;  
        }  
    }  
 
    // 读取一行数据。ignoreLF是“是否忽略换行符”  
    String readLine(boolean ignoreLF) throws IOException {  
        StringBuffer s = null;  
        int startChar;  
 
        synchronized (lock) {  
            ensureOpen();  
            boolean omitLF = ignoreLF || skipLF;  
 
            bufferLoop:  
            for (;;) {  
 
                if (nextChar >= nChars)  
                    fill();  
                if (nextChar >= nChars) { /* EOF */ 
                    if (s != null && s.length() > 0)  
                        return s.toString();  
                    else 
                        return null;  
                }  
                boolean eol = false;  
                char c = 0;  
                int i;  
 
                /* Skip a leftover '\n', if necessary */ 
                if (omitLF && (cb[nextChar] == '\n'))  
                    nextChar++;  
                skipLF = false;  
                omitLF = false;  
 
            charLoop:  
                for (i = nextChar; i < nChars; i++) {  
                    c = cb[i];  
                    if ((c == '\n') || (c == '\r')) {  
                        eol = true;  
                        break charLoop;  
                    }  
                }  
 
                startChar = nextChar;  
                nextChar = i;  
 
                if (eol) {  
                    String str;  
                    if (s == null) {  
                        str = new String(cb, startChar, i - startChar);  
                    } else {  
                        s.append(cb, startChar, i - startChar);  
                        str = s.toString();  
                    }  
                    nextChar++;  
                    if (c == '\r') {  
                        skipLF = true;  
                    }  
                    return str;  
                }  
 
                if (s == null)  
                    s = new StringBuffer(defaultExpectedLineLength);  
                s.append(cb, startChar, i - startChar);  
            }  
        }  
    }  
 
    // 读取一行数据。不忽略换行符  
    public String readLine() throws IOException {  
        return readLine(false);  
    }  
 
    // 跳过n个字符  
    public long skip(long n) throws IOException {  
        if (n < 0L) {  
            throw new IllegalArgumentException("skip value is negative");  
        }  
        synchronized (lock) {  
            ensureOpen();  
            long r = n;  
            while (r > 0) {  
                if (nextChar >= nChars)  
                    fill();  
                if (nextChar >= nChars) /* EOF */ 
                    break;  
                if (skipLF) {  
                    skipLF = false;  
                    if (cb[nextChar] == '\n') {  
                        nextChar++;  
                    }  
                }  
                long d = nChars - nextChar;  
                if (r <= d) {  
                    nextChar += r;  
                    r = 0;  
                    break;  
                }  
                else {  
                    r -= d;  
                    nextChar = nChars;  
                }  
            }  
            return n - r;  
        }  
    }  
 
    // “下一个字符”是否可读  
    public boolean ready() throws IOException {  
        synchronized (lock) {  
            ensureOpen();  
 
            // 若忽略换行符为true;  
            // 则判断下一个符号是否是换行符,若是的话,则忽略  
            if (skipLF) {  
                if (nextChar >= nChars && in.ready()) {  
                    fill();  
                }  
                if (nextChar < nChars) {  
                    if (cb[nextChar] == '\n')  
                        nextChar++;  
                    skipLF = false;  
                }  
            }  
            return (nextChar < nChars) || in.ready();  
        }  
    }  
 
    // 始终返回true。因为BufferedReader支持mark(), reset()  
    public boolean markSupported() {  
        return true;  
    }  
 
    // 标记当前BufferedReader的下一个要读取位置。关于readAheadLimit的作用,参考后面的说明。  
    public void mark(int readAheadLimit) throws IOException {  
        if (readAheadLimit < 0) {  
            throw new IllegalArgumentException("Read-ahead limit < 0");  
        }  
        synchronized (lock) {  
            ensureOpen();  
            // 设置readAheadLimit  
            this.readAheadLimit = readAheadLimit;  
            // 保存下一个要读取的位置  
            markedChar = nextChar;  
            // 保存“是否忽略换行符”标记  
            markedSkipLF = skipLF;  
        }  
    }  
 
    // 重置BufferedReader的下一个要读取位置,  
    // 将其还原到mark()中所保存的位置。  
    public void reset() throws IOException {  
        synchronized (lock) {  
            ensureOpen();  
            if (markedChar < 0)  
                throw new IOException((markedChar == INVALIDATED)  
                                      ? "Mark invalid" 
                                      : "Stream not marked");  
            nextChar = markedChar;  
            skipLF = markedSkipLF;  
        }  
    }  
 
    public void close() throws IOException {  
        synchronized (lock) {  
            if (in == null)  
                return;  
            in.close();  
            in = null;  
            cb = null;  
        }  
    }  
} 

InputStreamReader类

InputStreamReader 将字节流转换为字符流。是字节流通向字符流的桥梁,能将字节流输出为字符流,并且能为字节流指定字符集,可输出一个个的字符。如果不指定字符集编码,该解码过程将使用平台默认的字符编码,如:GBK。

源码分析

public class InputStreamReader extends Reader {
// 流解码类,所有的调用都是交给它完成。
    private final StreamDecoder sd;

  // 使用默认的字符集名来创建实例
    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);
    }

  
    // 根据指定的字符集来创建实例 
    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);
    }

 
    // 查看流是否准备好用于读取。 
    public boolean ready() throws IOException {
        return sd.ready();
    }
  // 关闭Reader    
    public void close() throws IOException {
        sd.close();
    }
}

例子

public static void main(String[] args) throws IOException {
    bufferReader(); 
    fileReader();
    inputStreamReader();
}

private static void bufferReader()throws IOException{
    BufferedReader buf = null ;        // 声明对象
    buf = new BufferedReader(new InputStreamReader(System.in)) ;    // 将字节流变为字符流
    String str = null ;    // 接收输入内容
    System.out.print("请输入内容:") ;
    try{
        str = buf.readLine() ;    // 读取一行数据
    }catch(IOException e){
        e.printStackTrace() ;    // 输出信息
    }
    System.out.println("输入的内容为:" + str) ;
}

private static void fileReader() throws IOException {
    char[] buf = new char[1024];  //字符数组
    FileReader in = new FileReader("D:\\hello.txt");
    int len = in.read(buf);  //此时的read方法可以读取一个字符或几个字符,len代表实际读取到的字符的个数。
    System.out.println(new String(buf,0,1024)); //String构造函数把字符数组转化为字符串。
    in.close();
}
public static void inputStreamReader() throws IOException {
    //读取字节流
    //InputStream in = System.in;//读取键盘的输入。
    InputStream in = new FileInputStream("D:\\hello.txt");//读取文件的数据。
    //将字节流向字符流的转换。要启用从字节到字符的有效转换,
    InputStreamReader isr = new InputStreamReader(in);//读取
    //InputStreamReader isr = new InputStreamReader(
    //new FileInputStream("D:\\demo.txt"));

    char []cha = new char[1024];
    int len = isr.read(cha);
    System.out.println(new String(cha,0,len));
    isr.close();

}