Java BufferedWriter BufferedReader 源碼分析_java

來源:互聯網
上載者:User

一: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進行操作、這樣不但效率、還能節省資源。最後、在程式中、出於效率的考慮、也應為低級流使用這兩個類進行裝飾一下、而不是直接拿著流直接上、覺得能實現就行。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.