| 維度 | 字節流 | 字符流 |
|---|---|---|
| 基礎類 | InputStream / OutputStream | Reader / Writer |
| 處理單位 | 字節 | 字符 |
| 編碼感知 | 無 | 有(內部用字節流+編碼轉換) |
| 適用數據 | 所有二進制數據(圖片、視頻、壓縮包、序列化對象) | 文本數據(源碼、配置、日誌、XML/JSON) |
| 典型實現 | FileInputStream, ByteArrayInputStream | FileReader, StringReader |
// FileReader 內部實現簡化版
public class FileReader extends InputStreamReader {
public FileReader(File file) throws FileNotFoundException {
super(new FileInputStream(file)); // 包裝字節流
}
public FileReader(String fileName) throws FileNotFoundException {
super(new FileInputStream(fileName));
}
}
本質:Reader/Writer = InputStream/OutputStream + CharsetDecoder/Encoder。
new InputStreamReader(is, StandardCharsets.UTF_8)FileChannel.transferTo() (零拷貝) 或大緩衝區字節流BufferedReader.readLine()
// 原文測試:FileReader 讀取 appletviewer.exe
int in = (char) freader.read(); // 強制轉 char,高位字節截斷!
fwriter.write(in); // 寫入截斷後的字符
失敗根因:
FileReader.read() 返回 int(0-65535 或 -1),代表 Unicode 代碼點(char) in 截斷高位,FileWriter.write(char) 再按編碼寫入,**不可逆損壞原始字節序列**
// 字節流 + 大緩衝區(推薦)
try (FileInputStream in = new FileInputStream(src);
FileOutputStream out = new FileOutputStream(dest)) {
byte[] buffer = new byte[8192];
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
}
// NIO 零拷貝(大文件最快)
try (FileChannel in = FileChannel.open(src, StandardOpenOption.READ);
FileChannel out = FileChannel.open(dest, StandardOpenOption.WRITE, StandardOpenOption.CREATE)) {
in.transferTo(0, in.size(), out);
}
| 無緩衝 | 有緩衝 | 提升原理 |
|---|---|---|
FileInputStream.read() 每次系統調用 | BufferedInputStream.read() 批量讀入內存 | 減少用戶態/內核態切換,利用磁盤預讀 |
FileWriter.write(char) 每字符刷新 | BufferedWriter.write() 累積刷新 | 合併系統調用,減少磁盤寫入次數 |
鐵律:幾乎所有文件 I/O 都應添加緩衝層(BufferedInputStream/OutputStream, BufferedReader/Writer)。
// 危險:依賴平台默認編碼(Windows GBK, Linux UTF-8)
FileReader reader = new FileReader("file.txt"); // 隱式使用 Charset.defaultCharset()
// 安全:顯式指定編碼
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream("file.txt"), StandardCharsets.UTF_8))) {
// ...
}
EF BB BF,Java 標準讀取器不會自動剝離BOMInputStream (Apache Commons IO) 或手動跳過
// 讀取所有行(自動關閉、指定編碼)
List lines = Files.readAllLines(Paths.get("file.txt"), StandardCharsets.UTF_8);
// 流式處理大文件(懶加載、自動關閉)
try (Stream lines = Files.lines(Paths.get("file.txt"), StandardCharsets.UTF_8)) {
lines.filter(l -> l.contains("ERROR")).forEach(System.out::println);
}
// 複製/移動/刪除(原子操作選項)
Files.copy(src, dest, StandardCopyOption.REPLACE_EXISTING);
Path:不可變、線程安全、支持符號鏈接、更豐富的文件系統操作File:歷史包袱、部分方法拋出檢查異常、無符號鏈接支持Java IO 選擇本質是數據性質決定 API:
InputStream/OutputStream + Buffered*)Reader/Writer + Buffered* + 顯式編碼)理解 Reader/Writer 內部封裝 Stream + Charset 的本質,掌握緩衝裝飾器模式、編碼顯式聲明、現代 NIO.2 API,是高質量 Java IO 代碼的基石。