Java I/O 是後端開發的基石,卻常被「流太多、易混淆、不知如何選型」所困擾。本文從設計模式、核心抽象、選型決策樹三個維度,構建完整的 I/O 知識體系,附贈高頻場景代碼模板。
| 抽象類 | 數據單位 | 流向 | 典型子類 |
|---|---|---|---|
| InputStream | 字節 (byte) | 讀入 | FileInputStream, ByteArrayInputStream, GZIPInputStream |
| OutputStream | 字節 (byte) | 寫出 | FileOutputStream, ByteArrayOutputStream, GZIPOutputStream |
| Reader | 字符 (char) | 讀入 | FileReader, BufferedReader, InputStreamReader |
| Writer | 字符 (char) | 寫出 | FileWriter, BufferedWriter, OutputStreamWriter |
| 維度 | 字節流 | 字符流 |
|---|---|---|
| 讀取單位 | 一次讀 1 字節 | 按編碼表讀 1~多字節組成 1 char |
| 適用數據 | 所有二進制:圖片、視頻、音頻、序列化對象 | 純文本:XML、JSON、CSV、日誌、屬性文件 |
| 編碼處理 | 無感知,原樣傳輸 | 內建編碼轉換(默認平台編碼,建議顯式指定 UTF-8) |
| 性能 | 無轉換開銷 | 編碼/解碼額外 CPU |
選型鐵律:處理純文本 → 字符流(Reader/Writer);其他一切 → 字節流(InputStream/OutputStream)。
Java I/O 大量使用裝飾者模式,通過層層包裝賦予流新能力:
// 經典組合:文件 → 緩衝 → 字符編碼 → 行讀取
BufferedReader reader = new BufferedReader(
new InputStreamReader(
new FileInputStream("data.txt"), StandardCharsets.UTF_8
)
);
// 寫出組合:文件 → 緩衝 → 字符編碼
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream("out.txt"), StandardCharsets.UTF_8
)
);
| 裝飾器 | 賦能 | 適用流 |
|---|---|---|
| BufferedInputStream/OutputStream | 緩衝,減少系統調用 | 字節流 |
| BufferedReader/Writer | 緩衝 + readLine() | 字符流 |
| DataInputStream/OutputStream | 基本類型序列化 | 字節流 |
| ObjectInputStream/OutputStream | 對象序列化 | 字節流 |
| GZIPInputStream/OutputStream | 壓縮/解壓 | 字節流 |
| InputStreamReader/OutputStreamWriter | 字節⇄字符橋樑(指定編碼) | 跨層轉換 |
| PrintStream/PrintWriter | 格式化輸出、不拋異常 | 字節/字符流 |
// 讀取所有行(自動關閉、指定編碼)
List<String> lines = Files.readAllLines(Paths.get("data.txt"), StandardCharsets.UTF_8);
// 大文件流式處理(常數內存)
try (Stream<String> stream = Files.lines(path, StandardCharsets.UTF_8)) {
stream.filter(s -> s.startsWith("ERROR")).forEach(System.out::println);
}
// 複制/移動/刪除(原子性選項)
Files.copy(src, dest, StandardCopyOption.REPLACE_EXISTING);
Files.move(src, dest, StandardCopyOption.ATOMIC_MOVE);
Channel、Buffer、Selector// 讀取
public List<String> readLines(Path path) throws IOException {
return Files.readAllLines(path, StandardCharsets.UTF_8);
}
// 寫入(覆蓋模式)
public void writeLines(Path path, Collection<String> lines) throws IOException {
Files.write(path, lines, StandardCharsets.UTF_8,
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
}
public void copyLargeFile(Path src, Path dest) throws IOException {
try (InputStream in = new BufferedInputStream(Files.newInputStream(src));
OutputStream out = new BufferedOutputStream(Files.newOutputStream(dest))) {
byte[] buffer = new byte[8192];
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
}
}
public void serialize(Object obj, Path file) throws IOException {
try (ObjectOutputStream oos = new ObjectOutputStream(
new BufferedOutputStream(Files.newOutputStream(file)))) {
oos.writeObject(obj);
}
}
public Object deserialize(Path file) throws IOException, ClassNotFoundException {
try (ObjectInputStream ois = new ObjectInputStream(
new BufferedInputStream(Files.newInputStream(file)))) {
return ois.readObject();
}
}
new FileReader(file) → 不指定編碼,跨平台亂碼隱患new InputStreamReader(Files.newInputStream(file), StandardCharsets.UTF_8)close() 易漏 → ✅ try-with-resources 自動關閉(Java 7+)available() 判斷文件大小 → 不可靠(網絡流、壓縮流返回 0)Files.size(path) 或 File.length()serialVersionUID → 版本升級反序列化失敗private static final long serialVersionUID = 1L;Java I/O 體系雖龐大,核心邏輯清晰:字節流處理二進制,字符流處理文本;裝飾者模式靈活組合能力;NIO.2 Files API 簡化文件操作;NIO 非阻塞支撐高併發網絡。掌握選型決策樹與標準代碼模板,即能應對 95% 的 I/O 開發場景。