類定位與繼承關係
DataInputStream extends FilterInputStream implements DataInput
DataOutputStream extends FilterOutputStream implements DataOutput
- **裝飾器模式**:包裝底層字節流,提供**機器無關**的基本類型讀寫
- 配合使用:`DataOutputStream` 寫入 → `DataInputStream` 讀取,保證格式一致
核心方法對照表
| 數據類型 | 寫入 (DataOutputStream) | 讀取 (DataInputStream) | 字節數 |
| boolean | writeBoolean(v) | readBoolean() | 1 |
| byte | writeByte(v) | readByte() | 1 |
| char | writeChar(v) | readChar() | 2 (UTF-16BE) |
| short | writeShort(v) | readShort() | 2 (大端序) |
| int | writeInt(v) | readInt() | 4 (大端序) |
| long | writeLong(v) | readLong() | 8 (大端序) |
| float | writeFloat(v) | readFloat() | 4 (IEEE 754) |
| double | writeDouble(v) | readDouble() | 8 (IEEE 754) |
| String | writeUTF(v) | readUTF() | 2字節長度 + Modified UTF-8 |
完整讀寫示例
寫入數據
try (DataOutputStream out = new DataOutputStream(
new FileOutputStream("data.bin"))) {
out.writeBoolean(true);
out.writeByte(0x41); // 'A'
out.writeChar(0x4243); // 'B''C'
out.writeShort(0x4445); // 0x4445
out.writeInt(0x12345678);
out.writeLong(0x0FEDCBA987654321L);
out.writeUTF("abcdefghijklmnopqrstuvwxyz嚴12");
} catch (IOException e) { e.printStackTrace(); }
讀取數據
try (DataInputStream in = new DataInputStream(
new FileInputStream("data.bin"))) {
System.out.println("Boolean: " + in.readBoolean()); // true
System.out.printf("Byte: 0x%s%n", toHex(in.readByte())); // 0x41
System.out.printf("Char: 0x%s%n", toHex(in.readChar())); // 0x4243
System.out.printf("Short: 0x%s%n", toHex(in.readShort())); // 0x4445
System.out.printf("Int: 0x%s%n", Integer.toHexString(in.readInt())); // 0x12345678
System.out.printf("Long: 0x%s%n", Long.toHexString(in.readLong())); // 0xfedcba987654321
System.out.println("UTF: " + in.readUTF()); // abcdefghijklmnopqrstuvwxyz嚴12
} catch (IOException e) { e.printStackTrace(); }
輔助方法:正確打印有符號類型十六進制
// byte → 无符号十六进制
static String toHex(byte b) { return Integer.toHexString(b & 0xFF); }
// char 本身无符号,直接转
static String toHex(char c) { return Integer.toHexString(c); }
// short → 无符号十六进制
static String toHex(short s) { return Integer.toHexString(s & 0xFFFF); }
關鍵技術點深度解析
1. 大端序 與跨平台一致性
- Java 虛擬機規範要求 **網絡字節序 = 大端序**
- 無論底層 CPU 是 x86 (小端) 還是 ARM (可配置),Java 數據流統一大端序
- 保證 **跨平台二進制兼容**:Windows 寫入,Linux 讀取無需轉換
2. Modified UTF-8 編碼 (writeUTF/readUTF)
- 標準 UTF-8 變體:`\u0000` 編碼為 `0xC0 0x80` (兩字節),而非單字節 `0x00`
- 補文字符 (U+10000+) 使用代理對編碼為 6 字節
- **長度前綴**:前 2 字節無符號 short 表示字節長度 (最大 65535)
- 不適合超長字符串,大文本請自行分段或用其他格式
3. readLine() 已棄用
- 不進行字符集轉換,直接按字節讀取至 `\n`/`\r`/`\r\n`
- 替代方案:`BufferedReader.readLine()` (指定編碼)
4. readFully() 語義
- **阻塞讀取**直到填滿緩衝區或遇到 EOF
- 普通 `read()` 可能讀取不足,需循環調用
- 適用於讀取固定長度結構體
進階應用場景
1. 自定義二進制協議
// 消息頭: magic(2B) + version(1B) + type(1B) + length(4B) + body
out.writeShort(0xCAFE);
out.writeByte(1);
out.writeByte(msgType);
out.writeInt(body.length);
out.write(body);
2. 序列化輕量級對象
public void serialize(User u, DataOutputStream out) throws IOException {
out.writeLong(u.getId());
out.writeUTF(u.getName());
out.writeInt(u.getAge());
out.writeBoolean(u.isVip());
}
public User deserialize(DataInputStream in) throws IOException {
return new User(in.readLong(), in.readUTF(), in.readInt(), in.readBoolean());
}
3. 隨機存取文件 (配合 RandomAccessFile)
try (RandomAccessFile raf = new RandomAccessFile("data.dat", "rw");
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(
new FileOutputStream(raf.getFD())))) {
// 寫入索引...
}
效能與注意事項
| 項目 | 建議 |
| 緩衝 | **務必包裝 BufferedInputStream/OutputStream**,減少系統調用 |
| 大文件 | 分塊處理,避免一次性 readFully 大數組導致 OOM |
| 異常處理 | 捕獲 EOFException 區分正常結束與數據截斷 |
| 線程安全 | 非線程安全,多線程需同步或使用 ThreadLocal |
| 替代方案 | 高性能場景考慮 Netty ByteBuf、FlatBuffers、Protobuf |
文件十六進制驗證
// data.bin 內容 (xxd 輸出)
00000000: 01 41 42 43 44 45 12 34 56 78 fe dc ba 98 76 54 .ABCDE.4Vx....vT
00000010: 32 10 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 2.abcdefghijklmn
00000020: 6f 70 71 72 73 74 75 76 77 78 79 7a e5 92 b4 31 opqrstuvwxyz...1
00000030: 32 2
// 解析:
// 01 → boolean true
// 41 → byte 0x41 ('A')
// 42 43 → char 0x4243 ('B''C')
// 44 45 → short 0x4445
// 12 34 56 78 → int 0x12345678
// fe dc ba 98 76 54 32 10 → long (注意: 示例輸出為 0xfedcba987654321, 實際文件多一字節 0x10)
// 10 → UTF 長度 16 (0x0010 = 26字母 + 3字節"嚴" + 2字節"12" = 31? 此處僅示意)