Java IO 是後端開發的基石,從經典的 java.io 流式模型到 java.nio 通道/緩衝區、java.nio.file 現代文件系統 API,理解其架構演進、核心抽象、裝飾者模式組合規律、性能特徵,能讓你在文件處理、網絡通信、序列化、高併發服務器開發中做出最優技術選型。本文從架構、核心類、組合模式、進階特性、NIO 演進五個維度系統重構知識體系。
java.io (Stream 流式模型) java.nio (Channel/Buffer 塊模型)
├── InputStream (字節輸入) ├── Channel (雙向通道)
│ ├── FileInputStream │ ├── FileChannel (文件、內存映射、鎖)
│ ├── ByteArrayInputStream │ ├── SocketChannel (TCP 非阻塞)
│ ├── FilterInputStream (裝飾者基類) │ ├── ServerSocketChannel (接受連接)
│ │ ├── BufferedInputStream (緩衝) │ ├── DatagramChannel (UDP)
│ │ ├── DataInputStream (基本類型) │ └── Pipe.SourceChannel (管道)
│ │ ├── ObjectInputStream (反序列化) │
│ │ └── ... ├── Buffer (容器)
│ ├── ObjectInputStream │ ├── ByteBuffer (核心、直接/堆內存)
│ └── PipedInputStream │ ├── CharBuffer, IntBuffer, LongBuffer...
│ │
├── OutputStream (字節輸出) ├── Selector (多路複用器)
│ ├── FileOutputStream │ └── SelectionKey (事件掩碼)
│ ├── ByteArrayOutputStream │
│ ├── FilterOutputStream ├── FileSystem / Path / Files (NIO.2 現代文件 API)
│ │ ├── BufferedOutputStream │ ├── 符號鏈接、屬性、ACL、遍歷、WatchService
│ │ ├── DataOutputStream │
│ │ ├── ObjectOutputStream └── AsynchronousFileChannel / AsynchronousSocketChannel (NIO.2 異步 IO)
│ │ └── ...
│ └── PipedOutputStream
│
├── Reader (字符輸入) 核心差異:
│ ├── FileReader 流式:順序、阻塞、字節/字符、裝飾者組合
│ ├── CharArrayReader 塊式:隨機訪問、非阻塞、緩衝區、通道、零拷貝、內存映射
│ ├── StringReader
│ ├── InputStreamReader (字節→字符 橋接) 選型:傳統 IO 簡單易用;NIO 高併發、大文件、零拷貝、非阻塞網絡
│ ├── BufferedReader (readLine)
│ └── ...
│
└── Writer (字符輸出)
├── FileWriter
├── CharArrayWriter
├── StringWriter
├── OutputStreamWriter (字符→字節 橋接)
├── BufferedWriter (newLine)
└── PrintWriter (format/println)
InputStream:read()/read(byte[])/read(byte[],off,len) 返回實際讀取字節數,-1 表示末尾。OutputStream:write(int)/write(byte[])/write(byte[],off,len)/flush()/close()。Reader:read() 返回字符 int (0~65535),-1 末尾;read(char[])/read(char[],off,len)。Writer:write(int)/write(char[])/write(String)/flush()/close()。// 節點流 (數據源/目的地)
FileInputStream fis = new FileInputStream("data.bin");
FileReader fr = new FileReader("data.txt");
// 處理流 (裝飾者鏈) - 由內向外包裹
// 字節流鏈:源 -> 緩衝 -> 數據類型 -> 對象
DataInputStream dis = new DataInputStream(
new BufferedInputStream(
new FileInputStream("data.bin")));
// 字符流鏈:源 -> 字節轉字符 -> 緩衝
BufferedReader br = new BufferedReader(
new InputStreamReader(
new FileInputStream("data.txt"), StandardCharsets.UTF_8));
// 輸出鏈
PrintWriter pw = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream("out.txt"), StandardCharsets.UTF_8)));
原則:緩衝裝飾者 (Buffered*) 必須套在節點流或轉換流外層,否則無緩衝效果。轉換流 (InputStreamReader/OutputStreamWriter) 橋接字節與字符,必須顯式指定 Charset。
| 流類 | 功能 | 核心方法 | 典型場景 |
|---|---|---|---|
BufferedInputStream/OutputStream | 字節緩衝 (默認 8KB) | read/write 批量 | 所有文件/網絡字節流必加 |
BufferedReader/Writer | 字符緩衝 + 行讀寫 | readLine()/newLine() | 文本文件逐行處理 |
DataInputStream/OutputStream | 基本類型序列化 (UTF-8 字符串) | readInt/readUTF/writeLong | 自定義二進制協議、數據文件 |
ObjectInputStream/OutputStream | Java 對象序列化 (實現 Serializable) | readObject/writeObject | RPC 參數、Session 持久化、緩存 |
PrintStream/PrintWriter | 格式化輸出、自動 flush、不拋異常 | println/printf/format | 控制台、日志、報表生成 |
PushbackInputStream/Reader | 回退/預讀 (一字節/字符) | unread() | 詞法分析器、解析器 |
SequenceInputStream | 合併多個輸入流 | Enumeration | 分片文件合併讀取 |
PipedInputStream/OutputStream | 線程間管道通信 (內存環形緩衝) | connect() | 生產者-消費者 (低性能,慎用) |
// 核心:InputStreamReader / OutputStreamWriter
// 讀取 GBK 文件
try (BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream("gbk.txt"), "GBK"))) {
String line;
while ((line = br.readLine()) != null) { /* ... */ }
}
// 寫入 UTF-8 文件 (無 BOM)
try (PrintWriter pw = new PrintWriter(
new OutputStreamWriter(new FileOutputStream("utf8.txt"), StandardCharsets.UTF_8))) {
pw.println("中文內容");
}
// 獲取平台默認編碼 (Java 18+ 默認 UTF-8)
Charset.defaultCharset(); // 或 System.getProperty("file.encoding")
// 字節字符串轉換
byte[] bytes = "中文".getBytes(StandardCharsets.UTF_8);
String str = new String(bytes, StandardCharsets.UTF_8);
編碼陷阱:中文 Windows 默認 GBK,Linux/Mac 默認 UTF-8。所有跨平台文件/網絡 IO 必須顯式指定 Charset。
// 模式:r (只讀) / rw (讀寫) / rws (同步元數據) / rwd (同步數據)
try (RandomAccessFile raf = new RandomAccessFile("data.dat", "rw")) {
raf.seek(100); // 移動文件指針
raf.writeInt(0xCAFEBABE); // 寫入 int
raf.seek(0);
System.out.println(raf.readLong()); // 讀取 long
// 文件鎖 (進程級)
FileLock lock = raf.getChannel().tryLock(0, 100, false); // 獨佔鎖前 100 字節
if (lock != null) {
try { /* 臨界區 */ } finally { lock.release(); }
}
}
適用:固定長度記錄文件、斷點續傳、簡易嵌入式數據庫、進程間文件鎖協調。
ByteBuffer buf = ByteBuffer.allocateDirect(1024); // 直接內存 (堆外),零拷貝關鍵
buf.put((byte) 1).putInt(2).putLong(3); // 寫入 (position 遞增)
buf.flip(); // 切換讀模式 (limit=position, position=0)
int b = buf.get(); // 讀取
buf.clear(); // 重置寫模式
buf.compact(); // 未讀數據移至頭部,繼續寫入 (流式處理常用)
try (FileChannel fc = FileChannel.open(Paths.get("big.dat"), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
// 內存映射 (大文件隨機訪問神器)
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, fc.size());
// 零拷貝傳輸 (通道間)
fc.transferTo(0, fc.size(), socketChannel); // 發送文件到網絡
// 或
socketChannel.transferFrom(fc, 0, fc.size());
// 文件鎖
FileLock lock = fc.lock(0, 100, false); // 阻塞獲取
}
Selector selector = Selector.open();
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(8080));
ssc.configureBlocking(false);
ssc.register(selector, SelectionKey.OP_ACCEPT);
while (selector.select() > 0) {
for (SelectionKey key : selector.selectedKeys()) {
if (key.isAcceptable()) {
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer buf = ByteBuffer.allocate(1024);
int n = sc.read(buf);
if (n > 0) { /* 處理數據 */ }
}
selector.selectedKeys().remove(key); // 必須手動移除
}
}
Path:不可變路徑對象,豐富運算 (resolve/relativize/normalize/subpath)。Files:靜態工具類,原子操作、屬性讀寫、遍歷、WatchService、ACL。FileSystem:抽象文件系統,支援 ZIP/JAR 作為文件系統掛載。// 監聽目錄變更
WatchService watcher = FileSystems.getDefault().newWatchService();
Paths.get("data").register(watcher, StandardWatchEventKinds.ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
while (true) {
WatchKey key = watcher.take();
for (WatchEvent> event : key.pollEvents()) {
System.out.println(event.kind() + " " + event.context());
}
key.reset();
}
| 需求 | 推薦方案 | 關鍵類 |
|---|---|---|
| 小文本文件讀寫 | Files.readString/writeString | Files |
| 大文本文件逐行處理 | Files.lines() Stream / BufferedReader | Files, BufferedReader |
| 二進制文件隨機讀寫 | RandomAccessFile / FileChannel + MappedByteBuffer | RandomAccessFile, FileChannel |
| 大文件高速複製/傳輸 | FileChannel.transferTo/From (零拷貝) | FileChannel |
| 高併發 TCP 服務器 | NIO Selector / Netty (生產必用框架) | Selector, SocketChannel |
| 異步文件 IO | AsynchronousFileChannel + CompletionHandler | AsynchronousFileChannel |
| 對象序列化 | ObjectOutputStream/InputStream / Protobuf/Kryo (高性能) | ObjectOutputStream |
| 跨平台路徑/屬性/遍歷/監聽 | Path / Files / WatchService | Path, Files |
| 線程間管道通信 | BlockingQueue / Disruptor (高性能) / Piped* (教學) | BlockingQueue |
StandardCharsets.UTF_8。Buffered*,默認 8KB 可調優。readAllBytes/readAllLines 載入大文件,使用 Stream/Channel 分塊處理。Java IO 經歷了三代演進:流式阻塞 (java.io) → 塊式非阻塞 (java.nio) → 現代文件系統 API (java.nio.file)。理解裝飾者模式組合邏輯、緩衝與轉換流的正確順序、字符編碼顯式化、NIO 核心三大組件 (Channel/Buffer/Selector) 協作機制、零拷貝與內存映射的適用邊界,是構建高性能、穩健、可移植 IO 系統的鑰匙。實際工程中:文件操作用 Files/Path,網絡高併發用 Netty,對象傳輸用 Protobuf,日誌用 Logback 異步 Appender,站在巨人肩膀上構建業務價值。