randomaccessfile 是 java 中唯一支持真正随机读写的 io 类,通过 file pointer 实现任意字节位置跳转,适用于断点续传、大文件分片等场景;需用 "rw" 模式、配合 seek() 和 length() 控制读写位置,并注意线程安全与资源释放。

RandomAccessFile 是 Java 中唯一支持真正随机读写(非顺序)的 IO 类,它内部维护一个文件指针(file pointer),可自由跳转到任意字节位置进行读或写,非常适合实现断点续传、大文件分片处理、日志追加+回溯等场景。
一、RandomAccessFile 的基本用法与定位控制
创建时需指定文件路径和访问模式("r" 只读、"rw" 读写、"rws" 同步元数据、"rwd" 同步内容),且必须处理 IOException:
RandomAccessFile raf = new RandomAccessFile("data.bin", "rw");
关键操作:
- raf.getFilePointer():获取当前指针偏移量(单位:字节)
- raf.seek(long pos):将指针移动到指定位置(支持向前/向后跳转)
- raf.length():返回文件总字节数(可用于判断是否已写完)
注意:seek() 不会触发实际 IO,只是改变逻辑位置;读写操作(如 readInt()、writeUTF())才真正从/向该位置执行。
二、实现断点续传的核心逻辑
断点续传本质是「记录已传输字节量 → 恢复时跳过已传部分 → 接着写」。核心在于:服务端保存已接收长度,客户端每次打开文件前 seek 到该位置。
示例(简化版客户端续传写入):
// 假设上次传到 10240 字节,存于 offset.txt 或数据库中
long resumeOffset = loadResumeOffset(); // 如 10240
RandomAccessFile raf = new RandomAccessFile("target.zip", "rw");
raf.seek(resumeOffset); // 关键:跳过已写部分
// 开始接收新数据流(如从 Socket 或 HTTP 分块)
byte[] buffer = new byte[8192];
int len;
while ((len = inputStream.read(buffer)) != -1) {
raf.write(buffer, 0, len); // 直接写入当前指针处
}
raf.close();
saveResumeOffset(raf.length()); // 更新已写总长度
要点:
- 务必用 "rw" 模式打开,只读模式无法写入
- seek() 必须在 write() 前调用,否则默认从开头覆盖
- 若文件不存在,raf 会自动创建;若存在且 seek 超出 length,write() 会扩展文件(类似追加)
- 建议配合 FileChannel.force(true) 强制刷盘,避免断电丢数据
三、安全与健壮性注意事项
RandomAccessFile 不是线程安全的,多线程并发读写同一实例会导致数据错乱。生产环境应:
- 每个线程独占一个 raf 实例(推荐)
- 若必须共享,用 synchronized(raf) 包裹读写块(但会降低并发性能)
- 始终放在 try-with-resources 或 finally 中 close(),防止句柄泄漏
- 处理 IOException 时区分类型:EOFException(读到末尾)、IOException(磁盘满/权限不足等),针对性重试或报错
小技巧:可用 raf.setLength(long newLength) 截断或预分配空间(如提前扩容避免频繁扩展)。
四、对比 FileInputStream/FileOutputStream 的关键差异
普通流是单向顺序的,而 RandomAccessFile 的能力不可替代:
- FileInputStream 只能从头读,不能倒带或跳转;raf 可任意 seek 读历史段
- FileOutputStream 默认覆盖写,即使 append=true 也只能追加到末尾;raf 可在中间修改(如修复某字段)
- raf 支持 readLine()(按 \n/\r\n 切分)、writeChars() 等便捷方法,适合结构化二进制协议解析
不适用场景:纯文本逐行处理(用 BufferedReader 更自然)、高吞吐批量复制(NIO 的 FileChannel + ByteBuffer 更高效)。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











