Java I/O 是後端開發的核心基本功。本文梳理 java.io 傳統流體系、java.nio 非阻塞模型、零拷貝與內存映射底層原理,構建完整知識圖譜。
InputStream/OutputStream — 處理二進制數據(圖片、視頻、序列化對象)。Reader/Writer — 處理文本,內部按 UTF-16(char)處理,涉及編解碼。| 類型 | 節點流(直達數據源) | 處理流/裝飾器(增強功能) |
|---|---|---|
| 字節輸入 | FileInputStream, ByteArrayInputStream, PipedInputStream | BufferedInputStream, DataInputStream, ObjectInputStream |
| 字節輸出 | FileOutputStream, ByteArrayOutputStream, PipedOutputStream | BufferedOutputStream, DataOutputStream, ObjectOutputStream |
| 字符輸入 | FileReader, CharArrayReader, StringReader, PipedReader | BufferedReader, InputStreamReader(橋接), LineNumberReader |
| 字符輸出 | FileWriter, CharArrayWriter, StringWriter, PipedWriter | BufferedWriter, OutputStreamWriter(橋接), PrintWriter |
裝飾器模式:new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8")) — 層層包裝,職責單一。
System.in(標準輸入/鍵盤) → InputStreamSystem.out(標準輸出/屏幕) → PrintStreamSystem.err(標準錯誤/屏幕) → PrintStreamJDK 1.4 引入,解決高併發下線程阻塞問題。
ByteBuffer 最常用;支援直接內存(allocateDirect)減少堆外拷貝。FileChannel, SocketChannel, ServerSocketChannel, DatagramChannel),數據只能 Buffer↔Channel 流動。傳統讀寫:磁盘 → 內核緩衝區 → 用戶緩衝區 → 內核 Socket 緩衝區 → 協議棧(4 次拷貝,2 次上下文切換)。
FileChannel.transferTo():內核空間直接 DMA 拷貝到 Socket 緩衝區,避免用戶態拷貝(mmap 或 sendfile 系統調用)。
MappedByteBuffer:fileChannel.map(MapMode.READ_ONLY, 0, size) 將文件映射到虛擬內存,頁面缺失時由 OS 自動載入,適合大文件隨機訪問。
FileLock lock = fileChannel.lock(start, length, shared); // 共享/獨占鎖,跨進程可見
lock.release();
// 1. 緩衝讀取文本
try (BufferedReader br = new BufferedReader(new FileReader(file, StandardCharsets.UTF_8))) {
br.lines().forEach(System.out::println);
}
// 2. 二進制拷貝(NIO 快速)
try (FileChannel in = FileChannel.open(src, StandardOpenOption.READ);
FileChannel out = FileChannel.open(dst, StandardOpenOption.WRITE, StandardOpenOption.CREATE)) {
in.transferTo(0, in.size(), out);
}
// 3. 非阻塞 Server
Selector selector = Selector.open();
ServerSocketChannel ssc = ServerSocketChannel.open().bind(new InetSocketAddress(8080));
ssc.configureBlocking(false);
ssc.register(selector, SelectionKey.OP_ACCEPT);
while (selector.select() > 0) {
for (SelectionKey key : selector.selectedKeys()) { /* handle */ }
}
MappedByteBuffer / RandomAccessFile。