数据流
数据流用于按 Java 基本数据类型读写二进制数据。它不是写文本,而是按照固定二进制格式写入 int、long、double、boolean 等值。
# 1. 核心类
数据流包括:
DataInputStreamDataOutputStream
它们通常包装在其他字节流外面。
try (DataOutputStream output = new DataOutputStream(
new BufferedOutputStream(new FileOutputStream("user.dat")))) {
output.writeInt(1001);
output.writeUTF("Alice");
output.writeDouble(98.5);
}
读取时必须按照相同顺序读取:
try (DataInputStream input = new DataInputStream(
new BufferedInputStream(new FileInputStream("user.dat")))) {
int id = input.readInt();
String name = input.readUTF();
double score = input.readDouble();
}
# 2. 读写顺序必须一致
数据流没有字段名,也没有自描述元信息。写入顺序和读取顺序必须完全一致。
写入:
output.writeInt(1);
output.writeLong(100L);
读取:
int a = input.readInt();
long b = input.readLong();
如果读取顺序写错,后续数据都会错位。
# 3. writeUTF 的特点
writeUTF 使用一种修改版 UTF-8 格式,并在前面写入长度信息。它适合 Java 程序内部配套使用,但不适合作为跨语言通用协议。
如果需要跨语言协议,通常应该选择:
- JSON
- Protocol Buffers
- Avro
- MessagePack
- 自定义明确字节序和长度字段的二进制协议
# 4. 适合场景
数据流适合:
- Java 程序内部简单二进制格式。
- 小型工具写入固定结构数据。
- 单机临时数据文件。
- 学习二进制协议编码思想。
不适合:
- 长期演进的数据格式。
- 多语言系统共享数据。
- 需要兼容不同版本的复杂业务对象。
- 需要可读性和可调试性的文件。
# 5. 和对象流的区别
数据流写的是基本类型和字符串,开发者自己维护字段顺序。
对象流写的是对象图,由 Java 序列化机制维护对象结构。
| 对比项 | 数据流 | 对象流 |
|---|---|---|
| 写入内容 | 基本类型、UTF 字符串 | Java 对象 |
| 格式 | 简单二进制 | Java 序列化格式 |
| 可读性 | 低 | 低 |
| 跨语言 | 差 | 很差 |
| 版本演进 | 需要手动设计 | 容易踩坑 |
# 专家实践清单
| 设计点 | 要求 | 原因 |
|---|---|---|
| 字段顺序 | 写入和读取完全一致 | 数据流没有字段名 |
| 字段类型 | 类型要严格匹配 | 读错类型会破坏后续解析 |
| 版本演进 | 手动设计版本号 | 新旧格式需要兼容 |
| 跨语言 | 谨慎使用 | 字节序和格式约定不透明 |
数据流协议通常长这样:
magic
version
fieldCount
field1
field2
checksum
# Tips 快问快答
Q:数据流主要处理什么数据? A:Java 基本类型和 UTF 字符串的二进制读写。
Q:DataInputStream 和 DataOutputStream 必须配套吗?
A:通常要配套使用,否则读取顺序和类型很容易不匹配。
Q:数据流适合复杂对象吗? A:不适合,复杂对象应使用明确协议或序列化框架。
Q:数据流写出的文件可读性如何? A:较差,是二进制格式,不适合人工直接编辑。
Q:为什么要设计版本号? A:字段变化后需要识别格式版本,避免旧数据无法读取。
Q:数据流能跨语言吗? A:可以但不推荐,需要严格约定字节序、字段顺序和编码。
Q:它和对象流最大的区别是什么? A:数据流只写基本值,对象流写对象图和类元数据。
Q:生产中数据流常见用途是什么? A:小型内部二进制格式、教学协议示例或性能敏感但格式简单的场景。
# 6. 总结
数据流适合按固定顺序读写基本类型,本质上是一种简单的二进制编码工具。它可以帮助理解协议设计,但在大型生产系统中,通常会被更成熟的序列化框架替代。