JDK源码:BufferedReader

Posted jdkSpring

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK源码:BufferedReader相关的知识,希望对你有一定的参考价值。

      BufferedReader是为了提供读的效率而设计的一个包装类,它可以包装字符流。可以从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取.

     BufferReader的作用是为其它Reader提供缓冲功能。创建BufferReader时,我们会通过它的构造函数指定某个Reader为参数。BufferReader会将该Reader中的数据分批读取,每次读取一部分到缓冲中;操作完缓冲中的这部分数据之后,再从Reader中读取下一部分的数据。

类名

public class BufferedReader extends Reader 

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 */
// 如果下个字符是换行符,则跳过--专用于readLine()方法里面控制private boolean skipLF = false;// 设置“标记”时,保存的skipLF的值--用于mark()方法的变量private boolean markedSkipLF = false;//默认的缓冲区大小private static int defaultCharBufferSize = 8192;//用于readLine()方法时初始化StringBuffer的初始容量private static int defaultExpectedLineLength = 80;

方法

// 创建“Reader”对应的BufferedReader对象,// sz是BufferedReader的缓冲区大小public BufferedReader(Reader inint sz)// 创建“Reader”对应的BufferedReader对象,// 默认的BufferedReader缓冲区大小是8kpublic BufferedReader(Reader in)// 确保“BufferedReader”是打开状态private void ensureOpen() throws IOException// 填充缓冲区函数。有以下两种情况被调用:// (01) 缓冲区没有数据时,通过fill()可以向缓冲区填充数据。// (02) 缓冲区数据被读完,需更新时,通过fill()可以更新缓冲区的数据。private void fill() throws IOException// 从BufferedReader中读取一个字符,该字符以int的方式返回public int read() throws IOException// 将缓冲区中的数据写入到数组cbuf中。// off是数组cbuf中的写入起始位置,len是写入长度private int read1(char[] cbuf, int off, int len) throws IOException// 对read1()的封装,添加了“同步处理”和“阻塞式读取”等功能public int read(char cbuf[], int off, int len) throws IOException// 读取一行数据。ignoreLF是“是否忽略换行符”String readLine(boolean ignoreLF) throws IOException// 读取一行数据。不忽略换行符public String readLine() throws IOException// 跳过n个字符public long skip(long n) throws IOException// “下一个字符”是否可读public boolean ready() throws IOException// 始终返回true。因为BufferedReader支持mark(), reset()public boolean markSupported()// 标记当前BufferedReader的下一个要读取位置。public void mark(int readAheadLimit) throws IOException// 重置BufferedReader的下一个要读取位置,// 将其还原到mark()中所保存的位置。public void reset() throws IOException// 关闭readerpublic void close() throws IOException

fill()

private void fill() throws IOException { // dst表示“cb中填充数据的起始位置”。 int dst; if (markedChar <= UNMARKED) { // 没有标记的情况,则设dst=0。 dst = 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; }}

read()

// 从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++]; } }}

readLine(boolean ignoreLF)

/** * 阅读一行文字。换行符('\n')、回车符('\r')或紧跟换行符的回车符. */String readLine(boolean ignoreLF) throws IOException { StringBuffer s = null;    int startChar; synchronized (lock) { ensureOpen(); boolean omitLF = ignoreLF || skipLF; //外循环bufferLoop bufferLoop:        for (;;) {            if (nextChar >= nChars)// 第一次读入缓冲区 fill(); if (nextChar >= nChars) { /* EOF */ //输入流in对象没有可读字符了 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【识别cb[]内部是否有换行符】 charLoop: for (i = nextChar; i < nChars; i++) { c = cb[i]; if ((c == '\n') || (c == '\r')) { eol = true; //跳出内循环,执行内循环下面的代码 break charLoop; }            } startChar = nextChar; // nextChar此时指向\n或\r或缓冲区末尾            nextChar = i; if (eol) { String str; if (s == null) { //范围为上一个nextChar的位置到\n或\r或缓冲区末尾的位置 str = new String(cb, startChar, i - startChar); } else { s.append(cb, startChar, i - startChar); str = s.toString(); } //跳过\n nextChar++; //跳过换行 if (c == '\r') { skipLF = true; } return str;            } if (s == null) s = new StringBuffer(defaultExpectedLineLength); s.append(cb, startChar, i - startChar); } }}


使用readLine()一定要注意:

  1. 1.读入的数据要注意有/r或/n或/r/n

  2. 2.没有数据时会阻塞,在文件读取结束和数据流异常或断开时才会返回null

  3. 3.使用socket之类的数据流时,要谨慎使用readLine(),以免为了等待一个换行/回车符而一直阻塞


代码注释

public class BufferedReader extends Reader {
private Reader in;//字符输入流
private char cb[];//字符缓冲区 //nChars:读取字符存储的结束下标,nextChar:读取字符存储的开始下标 private int nChars, nextChar;
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 */
// 如果下个字符是换行符,则跳过--专用于readLine()方法里面控制 private boolean skipLF = false;
// 设置标志时的markedSkipLF--用于mark()方法的变量 private boolean markedSkipLF = false;
//默认的缓冲区大小 private static int defaultCharBufferSize = 8192; //用于readLine()方法时初始化StringBuffer的初始容量 private static int defaultExpectedLineLength = 80;
/** * 创建一个指定缓冲区大小的缓冲字符输入流 * * @param in A Reader * @param sz Input-buffer size * * @exception IllegalArgumentException If {@code sz <= 0} */ public BufferedReader(Reader in, int sz) { super(in); if (sz <= 0) throw new IllegalArgumentException("Buffer size <= 0"); this.in = in; cb = new char[sz]; //初始从0开始读 nextChar = nChars = 0; }
/** * 创建一个默认缓冲区大小的缓冲字符输入流 * * @param in A Reader */ public BufferedReader(Reader in) { this(in, defaultCharBufferSize); }
/** 检查输入流是否关闭 */ private void ensureOpen() throws IOException { if (in == null) throw new IOException("Stream closed"); }
/** * 填充输入缓冲区,并考虑标记是否有效. */ private void fill() throws IOException { int dst; if (markedChar <= UNMARKED) {//没有标记 dst = 0; } else { /* Marked */ int delta = nextChar - markedChar; if (delta >= readAheadLimit) { /* 超过预读限制:无效标记 */ markedChar = INVALIDATED; readAheadLimit = 0; dst = 0; } else { if (readAheadLimit <= cb.length) { /* Shuffle in the current buffer */ System.arraycopy(cb, markedChar, cb, 0, delta); markedChar = 0; dst = delta; } else { /* 将内部缓冲字符数组cb[]扩容至readAheadLimit大小*/ char ncb[] = new char[readAheadLimit]; System.arraycopy(cb, markedChar, ncb, 0, delta); cb = ncb; markedChar = 0; dst = delta; } nextChar = nChars = delta; } }
int n; do { //从输入源对象in读取cb剩余空闲个数的字符,从dst索引处开始写入 n = in.read(cb, dst, cb.length - dst); } while (n == 0); if (n > 0) { //读满cb[] nChars = dst + n; nextChar = dst; } }
/** * Reads a single character. * * @return The character read, as an integer in the range * 0 to 65535 (<tt>0x00-0xffff</tt>), or -1 if the * end of the stream has been reached * @exception IOException If an I/O error occurs */ public int read() throws IOException { synchronized (lock) { ensureOpen(); for (;;) { if (nextChar >= nChars) { fill(); if (nextChar >= nChars) return -1; } if (skipLF) { skipLF = false; if (cb[nextChar] == '\n') { nextChar++; continue; } } return cb[nextChar++]; } } }
/**     * 将字符读入数组的一部分,必要时从底层流读取 */ 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读取的是cb.length+1,则返回-1 if (nextChar >= nChars) return -1; if (skipLF) { skipLF = false; //如果是换行,nextChar+1 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; }
/** * Reads characters into a portion of an array.     */ 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; } }
/** * 阅读一行文字。换行符('\n')、回车符('\r')或紧跟换行符的回车符.     */ String readLine(boolean ignoreLF) throws IOException { StringBuffer s = null;        int startChar; synchronized (lock) { ensureOpen(); boolean omitLF = ignoreLF || skipLF; //外循环bufferLoop bufferLoop:            for (;;) { if (nextChar >= nChars)//第一次读入内容 fill(); if (nextChar >= nChars) { /* EOF */ //输入流in对象没有可读字符了 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【识别cb[]内部是否有换行符】 charLoop: for (i = nextChar; i < nChars; i++) { c = cb[i]; if ((c == '\n') || (c == '\r')) { eol = true; //跳出内循环,执行内循环下面的代码 break charLoop; }                } startChar = nextChar; // nextChar此时指向\n或\r或缓冲区末尾                nextChar = i; if (eol) { String str; if (s == null) { //范围为上一个nextChar的位置到\n或\r或缓冲区末尾的位置 str = new String(cb, startChar, i - startChar); } else { s.append(cb, startChar, i - startChar); str = s.toString(); } //跳过\n 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();
/*             * 如果需要跳过换行符,             * 而下一个要读取的字符是换行符,那么就直接跳过它             */ if (skipLF) { /* 注意,in.ready()将返回true, * 前提是且仅当流上的下一次读取不会阻塞 */ if (nextChar >= nChars && in.ready()) { fill(); } if (nextChar < nChars) { if (cb[nextChar] == '\n') nextChar++; skipLF = false; } } return (nextChar < nChars) || in.ready(); } }
/** * Tells whether this stream supports the mark() operation, which it does. */ public boolean markSupported() { return true; }
/**     * 标记流中的当前位置。对reset()的后续调用将尝试将流重新定位到此点。     */ public void mark(int readAheadLimit) throws IOException { if (readAheadLimit < 0) { throw new IllegalArgumentException("Read-ahead limit < 0"); } synchronized (lock) { ensureOpen(); this.readAheadLimit = readAheadLimit; markedChar = nextChar; markedSkipLF = skipLF; } }
/**     * 将流重置为最新标记     */  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; try { in.close(); } finally { in = null; cb = null; } } }
/**     * 返回一个Stream  里面的元素为读到的所有行内容     */ public Stream<String> lines() { Iterator<String> iter = new Iterator<String>() { String nextLine = null;
@Override public boolean hasNext() { if (nextLine != null) { return true; } else { try { nextLine = readLine(); return (nextLine != null); } catch (IOException e) { throw new UncheckedIOException(e); } } }
@Override public String next() { if (nextLine != null || hasNext()) { String line = nextLine; nextLine = null; return line; } else { throw new NoSuchElementException(); } } }; return StreamSupport.stream(Spliterators.spliteratorUnknownSize( iter, Spliterator.ORDERED | Spliterator.NONNULL), false); }}






以上是关于JDK源码:BufferedReader的主要内容,如果未能解决你的问题,请参考以下文章

阅读JDK源码后,我有了优化它的冲动!

java缓冲字符字节输入输出流:java.io.BufferedReaderjava.io.BufferedWriterjava.io.BufferedInputStreamjava.io.(代码片段

如何进行 Java 代码阅读分析?

Android 逆向类加载器 ClassLoader ( 类加载器源码简介 | BaseDexClassLoader | DexClassLoader | PathClassLoader )(代码片段

C# 获得目录创建时间的源码片段

初识Spring源码 -- doResolveDependency | findAutowireCandidates | @Order@Priority调用排序 | @Autowired注入(代码片段