前言
Java IO 體系中,轉換流橋接字節與字符世界,數據流實現基本類型的二進制序列化,字節數組流提供內存級高速數據交換。本文通過源碼分析與實戰代碼,深度解析這三類流的核心機制與最佳實踐。
一、轉換流:字節與字符的橋樑
核心類
InputStreamReader:字節輸入流 → 字符輸入流
OutputStreamWriter:字節輸出流 → 字符輸出流
關鍵特性
- 支持指定字符編碼(構造函數參數),解決中文亂碼核心方案
- 可裝飾
BufferedReader/Writer 獲得緩衝與行級操作(readLine()/newLine())
- 獲取當前編碼:
getEncoding()
實戰:指定編碼寫入文件
try (FileOutputStream fos = new FileOutputStream("test_gbk.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK")) {
osw.write("轉換流練習:指定GBK編碼");
osw.flush();
System.out.println("實際編碼:" + osw.getEncoding()); // 輸出:GBK
}
實戰:控制台輸入讀取
try (InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr)) {
String line;
while ((line = br.readLine()) != null) {
if ("exit".equals(line)) break;
System.out.println("輸入:" + line);
}
}
二、數據流:基本類型的二進制序列化
核心類
DataInputStream / DataOutputStream
- 繼承自
InputStream/OutputStream,屬於處理流(裝飾器模式)
支持的數據類型
| 類型 | 寫入方法 | 讀取方法 |
| boolean | writeBoolean() | readBoolean() |
| byte | writeByte() | readByte() |
| char | writeChar() | readChar() |
| short | writeShort() | readShort() |
| int | writeInt() | readInt() |
| long | writeLong() | readLong() |
| float | writeFloat() | readFloat() |
| double | writeDouble() | readDouble() |
| String | writeUTF() | readUTF() |
關鍵特性
- 平台無關性:使用大端序(網絡字節序)存儲,跨平台一致
- 順序敏感:讀取順序必須與寫入順序完全一致
available() 可獲取剩餘可讀字節數
實戰:內存中基本類型序列化
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
DataInputStream dis = new DataInputStream(bais)) {
dos.writeDouble(Math.random());
dos.writeBoolean(true);
dos.flush();
System.out.println("可讀字節:" + dis.available()); // 9 = 8(double) + 1(boolean)
System.out.println("讀取 double:" + dis.readDouble());
System.out.println("讀取 boolean:" + dis.readBoolean());
}
// 輸出示例:
// 可讀字節:9
// 讀取 double:0.02241410199042526
// 讀取 boolean:true
三、字節數組流:內存級高速數據通道
核心類
ByteArrayInputStream:從內存字節數組讀取
ByteArrayOutputStream:向內存字節數組寫入
構造函數選擇
| 類 | 構造函數 | 適用場景 |
| ByteArrayInputStream | new ByteArrayInputStream(byte[] buf) | 讀取現有數組 |
| new ByteArrayInputStream(byte[] buf, int offset, int length) | 讀取數組片段 |
| ByteArrayOutputStream | new ByteArrayOutputStream() | 默認 32 字節,自動擴容 |
| new ByteArrayOutputStream(int size) | 預分配指定大小,減少擴容開銷 |
核心方法
toByteArray():獲取內部緩衝區副本(ByteArrayOutputStream)
toString() / toString(String charset):直接轉字符串
writeTo(OutputStream out):直接輸出到其他流,避免中間拷貝
reset():重置緩衝區,復用對象
實戰:流之間的數據傳遞
// 輸出流數據直接接入輸入流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (DataOutputStream dos = new DataOutputStream(baos)) {
dos.writeUTF("Hello Memory Stream");
dos.writeInt(2024);
}
byte[] data = baos.toByteArray();
try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
DataInputStream dis = new DataInputStream(bais)) {
System.out.println(dis.readUTF()); // Hello Memory Stream
System.out.println(dis.readInt()); // 2024
}
最佳實踐總結
- 轉換流:始終顯式指定編碼,避免平台默認編碼差異導致亂碼
- 數據流:嚴格保持讀寫順序一致,適合基本類型序列化,不適合複雜對象圖
- 字節數組流:內存數據交換首選,預估大小預分配緩衝區,大數據量注意 OOM 風險
- 資源管理:統一使用 try-with-resources 確保流正確關閉