首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java BufferedReader/BufferedWriter 源碼深度剖析:緩衝機制與高效 I/O 奧秘

     編輯:Java 相關知識

緩衝流設計初衷

直接操作 FileReader/FileWriter 每次 read/write 都觸發系統調用,磁盤 I/O 極慢。緩衝流在內存中劃分 char[] buf (默認 8192 字符),批量讀寫、減少系統調用,典型提升 10 倍以上吞吐量。

BufferedWriter 核心邏輯

// 關鍵字段
private Writer out;        // 底層字符輸出流
private char[] cb;         // 緩衝區
private int nextChar = 0;  // 下一個可寫入位置
private int nChars;        // 緩衝區大小

// write(int c) - 單字符寫入
public void write(int c) throws IOException {
    synchronized (lock) {
        ensureOpen();
        if (nextChar >= nChars) flushBuffer(); // 滿則刷新
        cb[nextChar++] = (char) c;
    }
}

// write(char[] cbuf, int off, int len) - 批量寫入
public void write(char[] cbuf, int off, int len) throws IOException {
    synchronized (lock) {
        ensureOpen();
        if (len >= nChars) {                    // 數據超過緩衝區,直寫底層
            flushBuffer();
            out.write(cbuf, off, len);
            return;
        }
        if (len > nChars - nextChar) flushBuffer(); // 剩餘空間不夠,先刷新
        System.arraycopy(cbuf, off, cb, nextChar, len);
        nextChar += len;
    }
}

// flushBuffer() - 核心刷新邏輯
void flushBuffer() throws IOException {
    synchronized (lock) {
        ensureOpen();
        if (nextChar == 0) return;
        out.write(cb, 0, nextChar); // 一次性寫入底層
        nextChar = 0;
    }
}

BufferedReader 核心邏輯

// 關鍵字段
private Reader in;
private char[] cb;
private int nextChar = 0, nChars = 0; // 當前讀取位置、有效數據長度

// fill() - 從底層批量填充緩衝區
private void fill() throws IOException {
    // ... 處理 mark/reset 邏輯 ...
    int n;
    do {
        n = in.read(cb, nChars, cb.length - nChars); // 讀滿為止
    } while (n == 0);
    if (n > 0) {
        nChars += n;
        nextChar = 0; // 重置讀取指針
    }
}

// read() - 單字符讀取
public int read() throws IOException {
    synchronized (lock) {
        ensureOpen();
        for (;;) {
            if (nextChar >= nChars) { fill(); if (nextChar >= nChars) return -1; }
            // 處理 \r\n 換行符統一
            if (skipLF) { skipLF = false; if (cb[nextChar] == '\n') { nextChar++; continue; } }
            return cb[nextChar++];
        }
    }
}

// readLine() - 按行讀取 (掃描 \n 或 \r\n)
public String readLine() throws IOException { return readLine(false); }
String readLine(boolean ignoreLF) throws IOException {
    StringBuffer s = null;
    for (;;) {
        if (nextChar >= nChars) fill();
        if (nextChar >= nChars) return (s != null && s.length() > 0) ? s.toString() : null;
        // 掃描行結束符
        int i = nextChar;
        for (; i < nChars; i++) {
            char c = cb[i];
            if (c == '\n' || c == '\r') break;
        }
        // 組裝行內容
        if (s == null) s = new StringBuffer(defaultExpectedLineLength);
        s.append(cb, nextChar, i - nextChar);
        nextChar = i + 1;
        if (i < nChars && cb[i] == '\r' && nextChar < nChars && cb[nextChar] == '\n') nextChar++;
        return s.toString();
    }
}

mark/reset 支持

  • mark(readAheadLimit):記錄當前 nextChar,允許在限制範圍內 reset() 回退。
  • 若超過 readAheadLimit,可能觸發緩衝區擴容或標記失效。

實戰:高效文件複製

public static void copyFile(File src, File dest) throws IOException {
    try (BufferedReader br = new BufferedReader(new FileReader(src));
         BufferedWriter bw = new BufferedWriter(new FileWriter(dest))) {
        char[] buf = new char[8192];
        int len;
        while ((len = br.read(buf)) != -1) {
            bw.write(buf, 0, len);
        }
        bw.flush(); // 確保數據落盤
    }
}

性能優化建議

  1. 默認緩衝區 (8KB) 通常足夠,除非明確知道平均行長極大,否則無需調大。
  2. 大文本文件處理:優先 Files.lines(path) (Stream API) 或 BufferedReader.lines() (Java 8+),支持並行流。
  3. 編碼顯式指定:new BufferedReader(new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8))。
  4. 關閉順序:先 flush() 再 close(),try-with-resources 自動保證。

總結

BufferedReader/Writer 通過 內存緩衝區 + 批量系統調用 實現數量級性能提升。源碼核心在於 fill()/flushBuffer() 兩大私有方法,配合 nextChar/nChars 指針管理。理解其機制,有助於排查「數據丟失、亂碼、死循環」等疑難雜癥,也能指導自定義緩衝邏輯(如帶進度條的上傳)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved