前言:記憶體中處理字符資料的高效選擇
當需要在記憶體中對字符陣列 (char[]) 進行流式讀寫時,CharArrayReader 與 CharArrayWriter 是最輕量、無 I/O 開銷的選擇。它們分別實作 Reader/Writer 介面,支援標準流操作(read、write、mark/reset、skip、toCharArray/toString),常用於單元測試、字符緩衝組裝、編碼轉換中介等場景。本文將系統整理兩類核心 API 並提供完整可執行範例。
一、CharArrayReader:字符陣列輸入流
核心特性
- 構造時直接包裝
char[],可指定 offset/length 讀取片段 - 支援
mark(int readAheadLimit) / reset() 隨機回滾 ready() 非阻塞檢查是否可讀(字符流專屬) - 無底層 I/O,操作純記憶體,極快
關鍵 API 速查
| 方法 | 說明 |
read() | 讀取單一字符,返回 int (0~65535),-1 表結束 |
read(char[] cbuf, int off, int len) | 批次讀取至字符陣列 |
skip(long n) | 跳過 n 字符,返回實際跳過數 |
mark(int readAheadLimit) | 標記當前位置,readAheadLimit 限制標記有效範圍 |
reset() | 回到 mark 位置 |
ready() | 是否可讀取而不阻塞(字符流總是 true,除非結束) |
close() | 釋放資源(字符流通常無實質動作) |
完整測試程式碼:mark/reset/skip 實戰
import java.io.*; public class CharArrayReaderDemo { private static final int LEN = 5; private static final char[] LETTERS = "abcdefghijklmnopqrstuvwxyz".toCharArray(); public static void main(String[] args) { test(); } private static void test() { try (CharArrayReader car = new CharArrayReader(LETTERS)) { // 1. 讀前 5 字符:a b c d e for (int i = 0; i < LEN; i++) { if (car.ready()) { char c = (char) car.read(); System.out.printf("%d : %c%n", i, c); } } // 2. 標記第 6 字符位置 (f) car.mark(0); // 參數 0 在 CharArrayReader 無實際限制 // 3. 跳過 5 字符 → 指標來到 k car.skip(5); // 4. 讀 5 字符 → klmno char[] buf = new char[LEN]; car.read(buf, 0, LEN); System.out.println("buf=" + new String(buf)); // 5. 重置回 mark 位置 (f) car.reset(); // 6. 再讀 5 字符 → fghij car.read(buf, 0, LEN); System.out.println("buf=" + new String(buf)); } catch (IOException e) { e.printStackTrace(); } } }
執行輸出
0 : a 1 : b 2 : c 3 : d 4 : e buf=klmno buf=fghij
二、CharArrayWriter:字符陣列輸出流
核心特性
- 內部維護可擴容的
char[] buf,自動擴容 (類似 StringBuilder) - 支援
append() 鏈式寫入、writeTo(Writer) 轉寫至其他流 toCharArray() / toString() 直接取得結果,無需手動轉換 reset() 可重置緩衝區重複使用,避免重建物件
關鍵 API 速查
| 方法 | 說明 |
write(int c) / write(char[]) / write(String) | 寫入單字符/陣列/字串 |
append(char) / append(CharSequence) | 鏈式追加,返回 this,支援流式 API |
toCharArray() | 返回當前內容副本 (新陣列) |
toString() | 返回當前內容字串 |
size() | 當前緩衝區字符數 |
writeTo(Writer out) | 將內容寫入目標 Writer |
reset() | 清空緩衝區,大小歸零,陣列保留供下次使用 |
完整測試程式碼:鏈式 append、toCharArray、writeTo
import java.io.*; public class CharArrayWriterDemo { private static final char[] LETTERS = "abcdefghijklmnopqrstuvwxyz".toCharArray(); public static void main(String[] args) { test(); } private static void test() { try (CharArrayWriter caw = new CharArrayWriter()) { // 1. 寫入單字符 'A' caw.write('A'); // 2. 寫入字串 "BC" caw.write("BC"); // 3. 寫入陣列片段 defgh (index 3, len 5) caw.write(LETTERS, 3, 5); // 4. 鏈式 append: '0' + "123456789" + ijkl (index 8, len 4) caw.append('0') .append("123456789") .append(new String(LETTERS), 8, 12); System.out.println("caw=" + caw); // toString() // 5. 取得長度與陣列副本 System.out.println("size=" + caw.size()); char[] copy = caw.toCharArray(); System.out.println("copy=" + new String(copy)); // 6. 寫入另一個 Writer CharArrayWriter caw2 = new CharArrayWriter(); caw.writeTo(caw2); System.out.println("caw2=" + caw2); } catch (IOException e) { e.printStackTrace(); } } }
執行輸出
caw=ABCdefgh0123456789ijkl size=22 copy=ABCdefgh0123456789ijkl caw2=ABCdefgh0123456789ijkl
三、實務應用場景與最佳實踐
| 場景 | 推薦類別 | 理由 |
| 單元測試模擬輸入 | CharArrayReader | 無需建立臨時檔案,直接包裝字串測試邏輯 |
| 字符緩衝組裝 (替代 StringBuilder) | CharArrayWriter | 實作 Writer 介面,可直接傳給需 Writer 的 API (如 XML/JSON 產生器) |
| 編碼轉換中介 | Reader → CharArrayWriter → byte[] | 先寫入字符流,再配合 CharsetEncoder 轉 byte[] |
| 大量字符處理需重複使用緩衝區 | CharArrayWriter.reset() | 避免頻繁建立新物件,減少 GC 壓力 |
四、常見誤區提醒
- mark(0) 參數無效:
CharArrayReader.mark(int) 的參數在實作中被忽略,標記永遠有效直到 reset 或流結束。 ready() 不代表有資料 - :字符流
ready() 僅表示「不會阻塞」,若流已結束也會返回 false,不能用於判斷「是否還有資料」,應改用 read() != -1。 - toCharArray() 返回副本:修改返回陣列不影響內部緩衝區,反之亦然。
- 非執行緒安全:兩類別皆非同步,多執行緒環境需自行同步或改用
StringWriter/StringReader (內部同步)。
總結
CharArrayReader / CharArrayWriter 是 Java 字符流體系中「純記憶體、零 I/O、高效能」的輕量實作。掌握 mark/reset 隨機存取與 append/writeTo/toCharArray 靈活輸出特性,能在測試、序列化、格式轉換等場景大幅簡化代碼並提升效能。下次需要「字串當流用」時,請優先考慮它們。