RandomAccessFile
RandomAccessFile 支持对文件进行随机访问。和普通流只能顺序读写不同,它可以移动文件指针,在指定位置读取或写入。
# 1. 基本用法
try (RandomAccessFile file = new RandomAccessFile("data.dat", "rw")) {
file.writeInt(100);
file.writeUTF("hello");
file.seek(0);
int value = file.readInt();
String text = file.readUTF();
}
构造模式:
"r":只读。"rw":读写。"rws":读写,并尽量同步内容和元数据。"rwd":读写,并尽量同步内容。
# 2. 文件指针
RandomAccessFile 维护一个文件指针。
long pointer = file.getFilePointer();
file.seek(1024);
每次读写都会推动指针移动。seek 可以跳到任意位置。
# 3. 修改指定位置内容
try (RandomAccessFile file = new RandomAccessFile("data.dat", "rw")) {
file.seek(8);
file.writeInt(999);
}
这适合修改固定长度记录。如果记录长度不固定,直接覆盖可能破坏后续数据。
# 4. 文件追加
try (RandomAccessFile file = new RandomAccessFile("app.log", "rw")) {
file.seek(file.length());
file.write("new line\n".getBytes(StandardCharsets.UTF_8));
}
不过普通追加日志通常使用 FileOutputStream(file, true) 或日志框架更合适。
# 5. 与 FileChannel 的关系
RandomAccessFile 可以获取对应的 FileChannel:
try (RandomAccessFile file = new RandomAccessFile("data.dat", "rw");
FileChannel channel = file.getChannel()) {
// use channel
}
FileChannel 提供了更现代的 NIO 能力,如内存映射、文件锁、通道传输。
# 6. 适合场景
- 固定长度记录文件。
- 断点续传。
- 文件分片下载。
- 简单索引文件。
- 需要从文件中间读写的场景。
不适合:
- 可变长度复杂记录。
- 高并发写入同一个文件。
- 需要事务语义的数据存储。
这些场景应该考虑数据库、日志结构存储或成熟文件格式。
# 专家实践清单
| 场景 | 是否适合 | 说明 |
|---|---|---|
| 固定格式文件 | 适合 | 可按偏移读写字段 |
| 断点续传 | 适合 | 可 seek 到指定位置 |
| 大文件随机读取 | 适合 | 避免从头顺序扫描 |
| 高并发事务写入 | 不适合 | 缺少事务和并发控制 |
| 复杂索引存储 | 谨慎 | 更适合数据库或专用存储 |
随机访问模型:
文件字节序列
├─ offset=0
├─ offset=1024
└─ offset=N
seek(offset) -> read/write
# Tips 快问快答
Q:RandomAccessFile 解决什么问题?
A:按文件偏移位置随机读写,而不是只能顺序读写。
Q:seek 的作用是什么?
A:移动文件指针到指定偏移位置。
Q:RandomAccessFile 是 InputStream 吗?
A:不是,它是独立类,但提供读写基本类型和字节的方法。
Q:模式 r 和 rw 有什么区别?
A:r 只读,rw 可读写并可能创建文件。
Q:随机写文件是否自动保证线程安全? A:不保证,多线程写同一文件需要外部同步或分区设计。
Q:它适合做数据库吗? A:不适合直接当数据库,需要索引、事务、恢复和并发控制等大量机制。
Q:和 FileChannel 有什么关系?
A:可以通过 getChannel() 获取底层 FileChannel。
Q:最常见的错误是什么? A:偏移计算错误、覆盖错误位置、并发写入没有协调。
# 7. 总结
RandomAccessFile 是顺序流之外的重要补充,适合按位置读写文件。它简单直接,但缺少高级并发和事务能力。复杂场景中通常会结合 FileChannel 或更专业的存储组件使用。