FileReader.read(char[])虽能批量读取,但因底层同步I/O和字符解码开销大、缓冲小、编码不明确,效率受限;需用BufferedReader包装并显式指定编码与大缓冲区,或改用InputStream+手动解码以提升性能。

FileReader 本身不支持 char 数组批量读取
直接调用 FileReader.read(char[]) 确实能批量读入字符,但底层仍基于 InputStreamReader 包装的字节流,每次读取都触发同步 I/O 和字符解码(如 UTF-8 → char),无法跳过解码开销。真正影响效率的不是“是否用 char 数组”,而是“解码是否必要”和“缓冲是否足够”。
- 如果源文件是纯 ASCII 或已知编码且无需实时字符语义(比如只做字符串匹配、长度统计),绕过
FileReader直接用FileInputStream+ 手动解码更可控 -
FileReader的read(char[])每次最多填满数组,但实际读到的字符数受编码边界限制(例如 UTF-8 中一个汉字占 3 字节,可能被截断),导致后续解码异常或丢数据 - 默认缓冲区仅 8192 字节,小数组反复调用
read()会放大系统调用开销
用 BufferedReader 包裹 FileReader 才能发挥 char 数组优势
单独用 FileReader 调用 read(char[]) 效果有限;必须配合 BufferedReader 提供的带缓冲的字符读取能力,才能减少底层 I/O 次数并稳定填充 char 数组。
- 构造时显式指定较大缓冲区:
new BufferedReader(new FileReader("a.txt"), 64 * 1024) - 循环调用
br.read(charBuf),而非fr.read(charBuf)—— 前者从内存缓冲区拷贝,后者直连文件句柄 - 注意返回值:
read(char[])返回实际读取的字符数,-1表示 EOF,不能假设每次填满数组 - 避免在循环内新建 char 数组,复用同一块内存(如
char[] buf = new char[8192])
char[] buf = new char[8192];
int n;
while ((n = br.read(buf)) != -1) {
// 处理 buf[0] 到 buf[n-1]
}当性能瓶颈在解码时,改用 InputStream + 自定义解码更高效
若文件编码固定(如 UTF-8)、且业务逻辑只关心字节位置或可预判字符边界(如处理 JSON、CSV),用 FileInputStream 配合 CharsetDecoder 手动解码,能避开 FileReader 的隐式同步和小缓冲缺陷。
- 用
ByteBuffer.allocateDirect(64 * 1024)减少堆内存拷贝 - 复用
CharsetDecoder实例,设置decoder.onMalformedInput(CodingErrorAction.REPLACE)避免异常中断 - 关键点:一次读字节、一批解码,而不是“读一字节→解一字符”这种低效模式
- 对纯 ASCII 文件,甚至可用
Arrays.equals()直接比字节,完全跳过解码
容易被忽略的陷阱:编码不一致导致 char 数组误读
FileReader 默认使用 Charset.defaultCharset(),而该值在不同机器上可能不同(Windows 是 GBK,Linux/macOS 通常是 UTF-8)。即使用了大 char 数组,一旦编码错,数组里就是乱码或 \uFFFD,后续所有优化都白费。
- 永远显式指定编码:不用
FileReader(file),改用new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8) - 不要依赖
file.encodingJVM 参数——它只影响FileReader,不影响其他组件,易造成行为不一致 - 如果文件头含 BOM(如 UTF-8-BOM),
FileReader不识别,需自行检测并跳过前 3 字节
真正快的不是“用 char 数组”,而是让解码只发生一次、缓冲足够大、编码始终明确——这些细节出问题,再大的数组也救不回效率。

















