Java NIO(New I/O)以 Channel(通道) 與 Buffer(緩衝區) 為核心抽象,實現了高效、非阻塞的 I/O 操作。不同於傳統 BIO 的流式處理,NIO 採用「通道-緩衝區」雙向數據傳輸模型:數據總是從 Channel 讀入 Buffer,或從 Buffer 寫入 Channel。本文系統梳理兩大核心組件原理、關鍵 API、典型應用場景,並附帶完整可運行代碼。
Channel 模擬傳統 I/O 中的「連接」,但具備雙向性、非阻塞性:
FileChannel 是文件 I/O 的主力通道,獲取途徑三種:
FileInputStream.getChannel()(唯讀)FileOutputStream.getChannel()(唯寫)RandomAccessFile.getChannel()(讀寫,支援隨機定位)package com.pichen.io;
import java.nio.*;
import java.nio.channels.*;
import java.io.*;
public class GetChannel {
private static final int BSIZE = 1024;
public static void main(String[] args) throws Exception {
// 1. FileOutputStream 寫入
FileOutputStream fos = new FileOutputStream("data.txt");
FileChannel fc = fos.getChannel();
fc.write(ByteBuffer.wrap("Some text ".getBytes()));
fos.close();
// 2. RandomAccessFile 追加
RandomAccessFile raf = new RandomAccessFile("data.txt", "rw");
fc = raf.getChannel();
fc.position(fc.size()); // 定位末尾
fc.write(ByteBuffer.wrap("Some more".getBytes()));
raf.close();
// 3. FileInputStream 讀取
FileInputStream fis = new FileInputStream("data.txt");
fc = fis.getChannel();
ByteBuffer buff = ByteBuffer.allocate(BSIZE);
fc.read(buff);
buff.flip(); // 切換讀模式
while (buff.hasRemaining()) {
System.out.print((char) buff.get());
}
fis.close();
}
}
NIO 提供 transferTo()/transferFrom() 直接在內核態傳輸數據,避免用戶態拷貝,大文件複製效能提升數倍:
FileChannel src = new FileInputStream("src.jpg").getChannel();
FileChannel dest = new FileOutputStream("dest.jpg").getChannel();
src.transferTo(0, src.size(), dest); // 一行搞定
Buffer 是固定大小的容器,內部維護四個關鍵指標:
| 屬性 | 含義 |
|---|---|
capacity | 固定容量,不可變 |
position | 下一個讀寫位置索引 |
limit | 讀模式:可讀數據上限;寫模式:等於 capacity |
mark | 標記位置,配合 reset() 使用 |
flip():寫→讀模式切換(limit=position, position=0)clear():重置為寫模式(position=0, limit=capacity),不清空數據compact():未讀數據前移,保留未讀內容,適用於「讀一些、寫一些」場景針對基本類型提供對應實現:
ByteBuffer(最常用,支援直接內存 allocateDirect)MappedByteBuffer(記憶體映射文件,大文件隨機訪問神器)CharBuffer, ShortBuffer, IntBuffer, LongBuffer, FloatBuffer, DoubleBufferRandomAccessFile raf = new RandomAccessFile("data.txt", "rw");
FileChannel fc = raf.getChannel();
ByteBuffer buf = ByteBuffer.allocate(1024);
while (fc.read(buf) != -1) {
buf.flip(); // 準備讀取
while (buf.hasRemaining()) {
System.out.print((char) buf.get());
}
buf.compact(); // 未讀數據前移,準備下輪寫入
}
raf.close();
channel.read(buffer) → buffer.flip() → buffer.get() → buffer.clear()/compact()buffer.put() → buffer.flip() → channel.write(buffer) → buffer.clear()channel.read(Buffer[]) / channel.write(Buffer[]) 實現多 Buffer 組合操作ByteBuffer.allocateDirect() 分配堆外內存,減少 GC 壓力,適合長生命周期、大數據量場景FileChannel.map(MapMode.READ_WRITE, 0, size) 讓 OS 虛擬內存管理文件,超大文件隨機讀寫首選transferTo() 利用 DMA 引擎,網絡傳輸(如 Netty)大量使用參考資源:Java NIO 系列教程(ifeve)