398. Java 文件操作基础 - 高级文件操作:随机访问文件
在 Java 中,随机访问文件(Random Access Files)允许你 非顺序地 读写文件内容,也就是说,你可以跳到文件中的任意位置读取或写入数据,而不必从头到尾顺序操作。
这种功能通常用于:
- 大型文件处理(只读取部分内容)
- 日志文件更新(在文件中间写入新数据)
- 数据库文件或二进制文件操作
1️⃣ 核心接口:SeekableByteChannel
SeekableByteChannel 是 NIO 提供的 可寻址通道,它扩展了 Channel I/O,并增加了当前位置概念。
常用方法:
| 方法 | 功能 |
|---|---|
position() | 获取通道当前的位置(偏移量) |
position(long) | 设置通道当前的位置(跳到文件某个偏移量) |
read(ByteBuffer) | 从当前通道位置读取数据到缓冲区 |
write(ByteBuffer) | 将缓冲区的数据写入当前通道位置 |
truncate(long) | 截断文件到指定长度 |
💡 小贴士:通过 Path.newByteChannel() 或 Files.newByteChannel() 可以获得 SeekableByteChannel 实例。在默认文件系统下,也可以将其 强转为 FileChannel,这样可以使用更多高级功能,如文件映射、文件锁、绝对位置读写等。
2️⃣ 示例讲解
假设我们有一个文件 file.txt,我们希望:
- 读取文件开头的 12 个字节
- 在开头写入
"I was here!" - 将文件开头的 12 个字节复制到文件末尾
- 再次写入
"I was here!"
import java.nio.file.*;
import java.nio.channels.FileChannel;
import java.nio.ByteBuffer;
import java.io.IOException;
import static java.nio.file.StandardOpenOption.*;
public class RandomAccessDemo {
public static void main(String[] args) {
Path file = Paths.get("file.txt");
String s = "I was here!\n";
byte[] data = s.getBytes();
ByteBuffer out = ByteBuffer.wrap(data); // 包装要写入的数据
ByteBuffer copy = ByteBuffer.allocate(12); // 用于存储文件开头的 12 字节
try (FileChannel fc = FileChannel.open(file, READ, WRITE)) {
// 1️⃣ 读取文件前 12 个字节
int nread;
do {
nread = fc.read(copy);
} while (nread != -1 && copy.hasRemaining());
// 2️⃣ 写入 "I was here!" 到文件开头
fc.position(0); // 跳到文件开头
while (out.hasRemaining()) {
fc.write(out);
}
out.rewind(); // 复位缓冲区,用于后续写入
// 3️⃣ 移动到文件末尾,并复制前 12 个字节到末尾
long length = fc.size();
fc.position(length); // 文件末尾
copy.flip(); // 切换为读模式
while (copy.hasRemaining()) {
fc.write(copy);
}
// 4️⃣ 如果需要再次写入 "I was here!" 到文件末尾,需要指定out.rewind();
while (out.hasRemaining()) {
fc.write(out);
}
System.out.println("文件操作完成!");
} catch (IOException e) {
System.err.println("I/O 异常: " + e.getMessage());
}
}
}
3️⃣ 核心讲解点
- 随机访问的关键是 position()
- 可以用
fc.position(偏移量)定位到文件任意位置 - 后续的
read()或write()都从这个位置开始
- 可以用
- 缓冲区管理(
ByteBuffer)- 写入前用
ByteBuffer.wrap() - 多次写入需要
out.rewind()或flip()来复位缓冲区
- 写入前用
FileChannel vs SeekableByteChannelFileChannel功能更强大,可实现 文件映射、锁定、绝对位置读写SeekableByteChannel简单操作即可满足随机访问需求
- 异常处理
- 所有 I/O 操作都可能抛出
IOException - 使用 try-with-resources 自动关闭通道,保证资源释放 ✅
- 所有 I/O 操作都可能抛出