DataOutputStream 写入基本类型
DataOutputStream 是 Java 中写入二进制基本类型数据的标准方式。
和 DataInputStream 配套使用,可以精确地保存和读取 int、long、double 等基本类型,不会像文本格式那样有精度损失或解析歧义。
基本用法
java
try (DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("data.bin")))) {
dos.writeInt(42); // 4 字节
dos.writeLong(1234567890L); // 8 字节
dos.writeDouble(3.14159); // 8 字节
dos.writeBoolean(true); // 1 字节
dos.writeUTF("Hello"); // 2 字节长度 + N 字节内容
}写入方法一览
| 方法 | 参数类型 | 字节数 |
|---|---|---|
writeBoolean(boolean) | boolean | 1 |
writeByte(int) | byte | 1 |
writeChar(int) | char | 2 |
writeShort(int) | short | 2 |
writeInt(int) | int | 4 |
writeLong(long) | long | 8 |
writeFloat(float) | float | 4 |
writeDouble(double) | double | 8 |
writeBytes(String) | String | N(忽略高 8 位) |
writeChars(String) | String | 2×N(每个字符 2 字节) |
writeUTF(String) | String | 2+N(长度前缀 + 内容) |
writeUTF() 的秘密
writeUTF() 是变长字符串写入的利器:
java
// 写入 "AB中"
// writeUTF:自动计算长度,中文正确
dos.writeUTF("AB中"); // [00,06,41,42,E4,BD,A0] 7字节
// ↑ 长度 = 6
// writeChars:每个字符 2 字节
dos.writeChars("AB中"); // [00,41,00,42,4E,2D] 6字节
// ↑ 每个字符 2 字节
// writeBytes:只写低 8 位,中文丢失
dos.writeBytes("AB中"); // [41,42] 2字节!推荐用 writeUTF():它自动添加长度前缀,读取时不需要预先知道字符串长度。
配合 Buffered 使用
永远配合 BufferedOutputStream 使用:
java
// ❌ 差:没有缓冲,每次写入都可能触发磁盘 IO
DataOutputStream dos = new DataOutputStream(
new FileOutputStream("data.bin"));
// ✅ 好:有缓冲
DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("data.bin")));实战:自定义文件格式
格式设计
文件头(固定 16 字节)
[Magic:4字节 "JCFG"] [版本号:4字节] [记录数:4字节] [保留:4字节]
记录(重复 N 次)
[ID:4字节] [名称:UTF字符串] [年龄:4字节] [分数:8字节]写入
java
public static void writeRecords(String path, List<Record> records)
throws IOException {
try (DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream(path)))) {
// 文件头
dos.writeBytes("JCFG"); // Magic
dos.writeInt(1); // 版本号
dos.writeInt(records.size()); // 记录数
dos.writeInt(0); // 保留字段
// 记录
for (Record r : records) {
dos.writeInt(r.id);
dos.writeUTF(r.name);
dos.writeInt(r.age);
dos.writeDouble(r.score);
}
}
}
static class Record {
int id;
String name;
int age;
double score;
}读取
java
public static List<Record> readRecords(String path) throws IOException {
List<Record> records = new ArrayList<>();
try (DataInputStream dis = new DataInputStream(
new BufferedInputStream(
new FileInputStream(path)))) {
// 读文件头
byte[] magic = new byte[4];
dis.readFully(magic);
if (!"JCFG".equals(new String(magic))) {
throw new IOException("Invalid file format");
}
int version = dis.readInt();
int count = dis.readInt();
dis.readInt(); // 跳过保留字段
// 读记录
for (int i = 0; i < count; i++) {
Record r = new Record();
r.id = dis.readInt();
r.name = dis.readUTF();
r.age = dis.readInt();
r.score = dis.readDouble();
records.add(r);
}
}
return records;
}常见错误
读写顺序不一致
java
// ❌ 写入顺序和读取顺序不一致
dos.writeInt(id);
dos.writeUTF(name);
dos.writeDouble(score);
// ❌ 读取顺序错了
int id = dis.readInt();
double score = dis.readDouble(); // 错!应该是 UTF
String name = dis.readUTF(); // 错!读到的是 score 的 8 字节当 UTF 解码flush()
DataOutputStream 没有缓冲,但底层 BufferedOutputStream 有:
java
try (DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("data.bin")))) {
dos.writeInt(42);
// 写完立即关闭,close() 会自动 flush
}
// 或者手动 flush
dos.flush();记住这个原则:
写什么读什么,写在前读在后。 DataInputStream 和 DataOutputStream 必须配对使用。
