一:BufferedWriter
1、類功能簡介:
BufferedWriter、緩存字符輸出流、他的功能是為傳入的底層字符輸出流提供緩存功能、同樣當使用底層字符輸出流向目的地中寫入字符或者字符數組時、每寫入一次就要打開一次到目的地的連接、這樣頻繁的訪問不斷效率底下、也有可能會對存儲介質造成一定的破壞、比如當我們向磁盤中不斷的寫入字節時、誇張一點、將一個非常大單位是G的字節數據寫入到磁盤的指定文件中的、沒寫入一個字節就要打開一次到這個磁盤的通道、這個結果無疑是恐怖的、而當我們使用BufferedWriter將底層字符輸出流、比如FileReader包裝一下之後、我們可以在程序中先將要寫入到文件中的字符寫入到BufferedWriter的內置緩存空間中、然後當達到一定數量時、一次性寫入FileReader流中、此時、FileReader就可以打開一次通道、將這個數據塊寫入到文件中、這樣做雖然不可能達到一次訪問就將所有數據寫入磁盤中的效果、但也大大提高了效率和減少了磁盤的訪問量!這就是其意義所在、 他的具體工作原理在這裡簡單提一下:這裡可能說的比較亂、具體可以看源碼、不懂再回頭看看這裡、當程序中每次將字符或者字符數組寫入到BufferedWriter中時、都會檢查BufferedWriter中的緩存字符數組buf(buf的大小是默認的或者在創建bw時指定的、一般使用默認的就好)是否存滿、如果沒有存滿則將字符寫入到buf中、如果存滿、則調用底層的writer(char[] b, int off, int len)將buf中的所有字符一次性寫入到底層out中、如果寫入的是字符數組、如果buf中已滿則同上面滿的時候的處理、如果能夠存下寫入的字符數組、則存入buf中、如果存不下、並且要寫入buf的字符個數小於buf的長度、則將buf中所有字符寫入到out中、然後將要寫入的字符存放到buf中(從下標0開始存放)、如果要寫入out中的字符超過buf的長度、則直接寫入out中、
2、BufferedWriter API簡介:
A:關鍵字private Writer out; 底層字符輸出流private char cb[]; 緩衝數組private int nChars, nextChar; nChars--cb的size,nextChar--cb中下一個字符的下標private static int defaultCharBufferSize = 8192; 默認cb大小private String lineSeparator;換行符、用於newLine方法。不同平台具有不同的值。 B:構造方法BufferedWriter(Writer out)使用默認cb大小創建BufferedWriter bw。 BufferedWriter(Writer out, int sz)使用默認cb大小創建BufferedWriter bw。 C:一般方法void close()關閉此流、釋放與此流有關的資源。 void flushBuffer()將cb中緩存的字符flush到底層out中、 void flush()刷新此流、同時刷新底層out流void newLine()寫入一個換行符。 void write(int c)將一個單個字符寫入到cb中。 void write(char cbuf[], int off, int len)將一個從下標off開始長度為len個字符寫入cb中void write(String s, int off, int len)將一個字符串的一部分寫入cb中
3、源碼分析
package com.chy.io.original.code;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");this.out = out;cb = new char[sz];nChars = sz;nextChar = 0;//獲取不同平台下的換行符表示方式。 lineSeparator =(String) java.security.AccessController.doPrivileged( new sun.security.action.GetPropertyAction("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);System.arraycopy(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、實例演示:與下面的BufferedReader結合使用實現字符類型的文件的拷貝。
二:BufferedReader
1、類功能簡介:
緩衝字符輸入流、他的功能是為傳入的底層字符輸入流提供緩衝功能、他會通過底層字符輸入流(in)中的字符讀取到自己的buffer中(內置緩存字符數組)、然後程序調用BufferedReader的read方法將buffer中的字符讀取到程序中、當buffer中的字符被讀取完之後、BufferedReader會從in中讀取下一個數據塊到buffer中供程序讀取、直到in中數據被讀取完畢、這樣做的好處一是提高了讀取的效率、二是減少了打開存儲介質的連接次數、詳細的原因下面BufferedWriter有說到。其有個關鍵的方法fill()就是每當buffer中數據被讀取完之後從in中將數據填充到buffer中、程序從內存中讀取數據的速度是從磁盤中讀取的十倍!這是一個很恐怖的效率的提升、同時我們也不能無禁止的指定BufferedReader的buffer大小、畢竟、一次性讀取in中耗時較長、二是內存價格相對昂貴、我們能做的就是盡量在其中找到合理點。一般也不用我們費這個心、創建BufferedReader時使用buffer的默認大小就好。
2、BufferedReader API簡介:
A:構造方法BufferedReader(Reader in, int sz)根據指定大小和底層字符輸入流創建BufferedReader。 br BufferedReader(Reader in)使用默認大小創建底層輸出流的緩衝流B:一般方法void close()關閉此流、釋放與此流有關的所有資源void mark(int readAheadLimit)標記此流此時的位置boolean markSupported()判斷此流是否支持標記void reset()重置in被最後一次mark的位置boolean ready()判斷此流是否可以讀取字符int read()讀取單個字符、以整數形式返回。如果讀到in的結尾則返回-1。 int read(char[] cbuf, int off, int len)將in中len個字符讀取到cbuf從下標off開始長度len中String readLine()讀取一行long skip(long n)丟棄in中n個字符
3、源碼分析
package com.chy.io.original.code;import java.io.IOException;/** * 為底層字符輸入流添加字符緩衝cb數組。提高效率* @version 1.1, 13/11/17 * @authorandyChen */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; /* Valid only when markedChar > 0 */ /** If the next character is a line feed, skip it */ 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");this.in = 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 */ System.arraycopy(cb, markedChar, cb, 0, delta); markedChar = 0; dst = delta;} else { /* Reallocate buffer to accommodate read-ahead limit */ char ncb[] = new char[readAheadLimit]; System.arraycopy(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 = Math.min(len, nChars - nextChar);System.arraycopy(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(); elsereturn 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(); this.readAheadLimit = 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;} }}4、實例演示:
package com.chy.io.original.test;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.File;import java.io.FileReader;import java.io.FileWriter;import java.io.IOException;public class BufferedWriterAndBufferedReaderTest {/** * 這裡對這兩個類的測試比較簡單、就是對文件字符流進行包裝、實現文件拷貝* 有興趣的可以測試一下效率、、偷個懶、、可無視*/public static void main(String[] args) throws IOException{File resouceFile = new File("D://test.txt");File targetFile = new File("E://copyOftest.txt");BufferedReader br = new BufferedReader(new FileReader(resouceFile));BufferedWriter bw = new BufferedWriter(new FileWriter(targetFile));char[] cbuf = new char[1024];int n = 0;while((n = br.read(cbuf)) != -1){bw.write(cbuf, 0, n);}//不要忘記刷新和關閉流、否則一方面資源沒有及時釋放、另一方面有可能照成數據丟失br.close();bw.flush();bw.close();}}總結:
對於BufferedReader、BufferedWriter、本質就是為底層字符輸入輸出流添加緩衝功能、先將底層流中的要讀取或者要寫入的數據先以一次讀取一組的形式來講數據讀取或者寫入到buffer中、再對buffer進行操作、這樣不但效率、還能節省資源。最後、在程序中、出於效率的考慮、也應為低級流使用這兩個類進行裝飾一下、而不是直接拿著流直接上、覺得能實現就行。