1 基本概括
2 主要介绍
2.1 高效流BufferedReader和BufferedWriter的优势
提高效率:为了提高字符流读写的效率,引入了缓冲机制,进行字符批量的读写,提高了单个字符读写的效率。BufferedReader用于加快读取字符的速度,BufferedWriter用于加快写入的速度。
简化了操作。
2.2 BufferedReader 缓冲区大小
BufferedReader和BufferedWriter类各拥有8192个字符的缓冲区。当BufferedReader在读取文本文件时,会先尽量从文件中读入字符数据并放满缓冲区,而之后若使用read()方法,会先从缓冲区中进行读取。如果缓冲区数据不足,才会再从文件中读取,使用BufferedWriter时,写入的数据并不会先输出到目的地,而是先存储至缓冲区中。如果缓冲区中的数据满了,才会一次对目的地进行写出。
2.3 ReusableBufferedReader:可重用的 BufferedReader
标准 Java 的弱点之一BufferedReader是它只能使用一次。一旦你关闭它,它就不再可用。如果需要读取大量文件或网络流,则必须为要读取的每个文件或网络流创建一个新的 BufferedReader。这意味着您正在创建一个新对象,更重要的是,创建一个新的 char 数组,该数组用作 BufferedReader 内的缓冲区。如果读取的文件或流的数量很高,并且它们被快速读取,这会给 Java 垃圾收集器带来压力。
另一种方法是创建一个可重用的 BufferedReader ,您可以在其中替换基础 source Reader,因此可以重用 BufferedReader 及其内部字节数组缓冲区。为了省去您的麻烦,我创建了这样一个ReusableBufferedReader,并在本教程的后面包含了它的代码。首先,我想向您展示如何使用它ReusableBufferedReader。
//构造一个ReusableBufferedReader(2M的内存缓存区) ReusableBufferedReader reusableBufferedReader = new ReusableBufferedReader(new char[1024 * 1024]); //创建来源 FileReader reader = new FileReader("/mydata;); reu(reader); 使用完后,ReusableBufferedReader您需要关闭它。 关闭它只会关闭底层源Reader。关闭 a 后,ReusableBufferedReader 您可以再次使用它,只需Reader在其上设置新源即可。 这是重用 a 的样子ReusableBufferedReader: reu(new FileReader("/mydata;)); //从 ReusableBufferedReader 读取数据 reu(); reu(new FileReader("/mydata;)); //从 ReusableBufferedReader 读取数据 reu();
2.4 readLine()方法的原理和newLine()的底层原理
readLine():无论是读一行,获取读取多个字符,其最终都是在硬盘上一个一个的读取。所以最终使用的还是read()方法一次读一个的方法。
newLine():根据系统来决定换行符
注意 不同的操作系统中换行符是不同的
* window:Windows系统里面,每行结尾是 回车+换行(CR+LF),即“\r\n”;
* linux:Unix系统里,每行结尾只有 换行CR,即“\n”;
* mac:Mac系统里,每行结尾是 回车CR 即'\r'。
2.5 Sy
从标准输入流Sy中直接读取使用者输入时,使用者每输入一个字符,Sy就读取一个字符。为了能一次读取一行使用者的输入,使用了BufferedReader来对使用者输入的字符进行缓冲。readLine()方法会在读取到使用者的换行字符时,再一次将整行字符串传入。
Sy是一个位流,为了转换为字符流,可使用InputStreamReader为其进行字符转换,然后再使用BufferedReader为其增加缓冲功能。
3 简单用例
3.1 进行文件的拷贝
@Test public void IOTest01() throws Exception{ BufferedReader br = new BufferedReader(new FileReader("D:\\javaio\;)); BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\javaio\\;)); // char[] cbuf = new char[1024]; // int len; // while((len=br.read(cbuf)) != -1){ // bw.write(cbuf, 0, len); // } //BufferedReader提供了readLine方法,可以不再使用字节读取方式 String readline; while((readline = br.readLine()) != null){ bw.write(readline); bw.newLine(); } bw.close(); br.close(); }
3.2 将文件追加到文件上
@Test public void IOTest2() throws Exception{ BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\;,true)); bw.write("我是测试用例!"); bw.close(); }
3.3 读取文件内容
@Test public void IOTest3() throws Exception{ BufferedReader br = new BufferedReader(new FileReader("D:\\;)); String readline; while((readline = br.readLine()) != null){ Sy(readline); } br.close(); }
4 源码
4.1 BufferedWriter源码分析
package com.c; import java.io.IOException; import java.io.PrintWriter; /** * 为字符输出流提供缓冲功能、提高效率。可以使用指定字符缓冲数组大小也可以使用默认字符缓冲数组大小。 */ public class BufferedWriter extends Writer { //底层字符输出流 private Writer out; //缓冲数组 private char cb[]; //nChars--cb中总的字符数,nextChar--cb中下一个字符的下标 private int nChars, nextChar; //默认cb大小 private static int defaultCharBufferSize = 8192; /** * Line separator string. This is the value of the line.separator * property at the moment that the stream was created. * 换行符、用于newLine方法。不同平台具有不同的值。 */ private String lineSeparator; /** * 使用默认cb大小创建BufferedWriter bw。 */ public BufferedWriter(Writer out) { this(out, defaultCharBufferSize); } /** * 使用指定cb大小创建br、初始化相关字段 */ public BufferedWriter(Writer out, int sz) { super(out); if (sz <= 0) throw new IllegalArgumentException("Buffer size <= 0"); = out; cb = new char[sz]; nChars = sz; nextChar = 0; //获取不同平台下的换行符表示方式。 lineSeparator = (String) java.( new ("line.separator")); } /** 检测底层字符输出流是否关闭*/ private void ensureOpen() throws IOException { if (out == null) throw new IOException("Stream closed"); } /** * 将cb中缓存的字符flush到底层out中、但是不flush底层out中的字符。 * 并且将cb清空。 */ void flushBuffer() throws IOException { synchronized (lock) { ensureOpen(); if (nextChar == 0) return; out.write(cb, 0, nextChar); nextChar = 0; } } /** * 将一个单个字符写入到cb中。 */ public void write(int c) throws IOException { synchronized (lock) { ensureOpen(); if (nextChar >= nChars) flushBuffer(); cb[nextChar++] = (char) c; } } /** * Our own little min method, to avoid loading java.lang.Math if we've run * out of file descriptors and we're trying to print a stack trace. */ private int min(int a, int b) { if (a < b) return a; return b; } /** * 将一个从下标off开始长度为len个字符写入cb中 */ 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) { /* 如果len大于cb的长度、那么就直接将cb中现有的字符和cbuf中的字符写入out中、 * 而不是写入cb、再写入out中 。 */ flushBuffer(); out.write(cbuf, off, len); return; } int b = off, t = off + len; while (b < t) { int d = min(nChars - nextChar, t - b); Sy(cbuf, b, cb, nextChar, d); b += d; nextChar += d; if (nextChar >= nChars) flushBuffer(); } } } /** * 将一个字符串的一部分写入cb中 */ 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(); } } } /** * 写入一个换行符。 */ public void newLine() throws IOException { write(lineSeparator); } /** * 刷新此流、同时刷新底层out流 */ public void flush() throws IOException { synchronized (lock) { flushBuffer(); out.flush(); } } /** * 关闭此流、释放与此流有关的资源。 */ public void close() throws IOException { synchronized (lock) { if (out == null) { return; } try { flushBuffer(); } finally { out.close(); out = null; cb = null; } } } }
4.2 BufferedReader 源码分析
package com.c; import java.io.IOException; /** * 为底层字符输入流添加字符缓冲cb数组。提高效率 */ public class BufferedReader extends Reader { private Reader in; private char cb[]; private int nChars, nextChar; private static final int INVALIDATED = -2; private static final int UNMARKED = -1; private int markedChar = UNMARKED; private int readAheadLimit = 0; private boolean skipLF = false; /** The skipLF flag when the mark was set */ private boolean markedSkipLF = false; private static int defaultCharBufferSize = 8192; private static int defaultExpectedLineLength = 80; /** * 根据指定大小和底层字符输入流创建BufferedReader。br */ public BufferedReader(Reader in, int sz) { super(in); if (sz <= 0) throw new IllegalArgumentException("Buffer size <= 0"); = in; cb = new char[sz]; nextChar = nChars = 0; } /** * 使用默认大小创建底层输出流的缓冲流 */ public BufferedReader(Reader in) { this(in, defaultCharBufferSize); } /** 检测底层字符输入流in是否关闭 */ private void ensureOpen() throws IOException { if (in == null) throw new IOException("Stream closed"); } /** * 填充cb。 */ private void fill() throws IOException { int dst; if (markedChar <= UNMARKED) { /* No mark */ dst = 0; } else { /* Marked */ int delta = nextChar - markedChar; if (delta >= readAheadLimit) { /* Gone past read-ahead limit: Invalidate mark */ markedChar = INVALIDATED; readAheadLimit = 0; dst = 0; } else { if (readAheadLimit <= cb.length) { /* Shuffle in the current buffer */ Sy(cb, markedChar, cb, 0, delta); markedChar = 0; dst = delta; } else { /* Reallocate buffer to accommodate read-ahead limit */ char ncb[] = new char[readAheadLimit]; Sy(cb, markedChar, ncb, 0, delta); cb = ncb; markedChar = 0; dst = delta; } nextChar = nChars = delta; } } int n; do { n = in.read(cb, dst, cb.length - dst); } while (n == 0); if (n > 0) { nChars = dst + n; nextChar = dst; } } /** * 读取单个字符、以整数形式返回。如果读到in的结尾则返回-1。 */ 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++]; } } } /** * 将in中len个字符读取到cbuf从下标off开始长度len中 */ private int read1(char[] cbuf, int off, int len) throws IOException { if (nextChar >= nChars) { /* If the requested length is at least as large as the buffer, and if there is no mark/reset activity, and if line feeds are not being skipped, do not bother to copy the characters into the local buffer. In this way buffered streams will cascade harmlessly. */ 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 = Ma(len, nChars - nextChar); Sy(cb, nextChar, cbuf, off, n); nextChar += n; return n; } /** * 将in中len个字符读取到cbuf从下标off开始长度len中 */ 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; } } /** * 从in中读取一行、是否忽略换行符 */ 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); } } } /** * 从in中读取一行、 */ public String readLine() throws IOException { return readLine(false); } /** * 丢弃in中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; } } /** * 判断cb中是否为空、或者底层in中是否有可读字符。 */ public boolean ready() throws IOException { synchronized (lock) { ensureOpen(); /* * If newline needs to be skipped and the next char to be read * is a newline character, then just skip it right away. */ if (skipLF) { /* Note that in.ready() will return true if and only if the next * read on the stream will not block. */ if (nextChar >= nChars && in.ready()) { fill(); } if (nextChar < nChars) { if (cb[nextChar] == '\n') nextChar++; skipLF = false; } } return (nextChar < nChars) || in.ready(); } } /** * 判断此流是否支持标记 */ public boolean markSupported() { return true; } /** * 标记此流此时的位置、当调用reset方法失效前最多允许读取readAheadLimit个字符。 */ public void mark(int readAheadLimit) throws IOException { if (readAheadLimit < 0) { throw new IllegalArgumentException("Read-ahead limit < 0"); } synchronized (lock) { ensureOpen(); = readAheadLimit; markedChar = nextChar; markedSkipLF = skipLF; } } /** * 重置in被最后一次mark的位置。即下一个字符从被最后一次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; } } }