FileChannel不支持真正双向读写,但可通过手动管理position和ByteBuffer状态(如flip/rewind)实现在同一通道上交替读写。关键点:读写串行、position自动推进、write前必须flip缓冲区。

FileChannel 本身不支持真正的“双向读写”——它不能像 RandomAccessFile 那样自由切换读写位置并同时进行读和写操作。但你可以通过合理控制 position、使用不同的缓冲区、配合 clear/flip 等 Buffer 操作,在同一个 FileChannel 上交替完成读与写,实现逻辑上的“双向操作”。关键在于:**读写是串行的,需手动管理文件指针和缓冲区状态。**
1. 基于同一个 FileChannel 交替读写(推荐方式)
打开一个可读可写的 Channel(如通过 RandomAccessFile.getChannel()),然后按需调用 read() 或 write(),每次操作前确保 position 正确:
- 调用 read(ByteBuffer) 前,ByteBuffer 必须处于 写模式(即 capacity ≥ limit,且 position = 0 或你期望的起始写入位置),读完后 buffer.position() 会更新为实际读取字节数;
- 调用 write(ByteBuffer) 前,必须先对 buffer 调用 flip(),使其从写模式转为读模式(limit 设为当前 position,position 归零),否则 write 会写入 0 字节;
- 每次 read/write 后,FileChannel 的 position 会自动前进对应字节数,下次操作将从此处开始(除非你显式调用 position(long) 修改)。
2. 手动控制文件指针实现“随机读写”
若需在文件任意位置读一段、再写一段(比如修改中间某块数据),不要依赖默认 position 自增,而是主动定位:
- 用 channel.position(1024) 跳到第 1024 字节处;
- 分配 ByteBuffer,read() 读取 64 字节 → 此时只影响 buffer,不影响 channel position(read 后 position 已+64);
- 再调用 channel.position(2048) 跳到别处,flip buffer 后 write() → 写入新内容;
- 注意:read/write 是阻塞且单次调用,不会自动循环,大文件需配合 while 循环 + check 返回值(-1 表示 EOF)。
3. 常见陷阱与规避方法
容易出错的地方集中在 Buffer 状态和 position 同步上:
- buffer.rewind() 或 buffer.clear() 不等于 channel.position() 归零 —— 前者只重置 buffer 内部指针,后者还重置 limit=capacity;channel position 是独立的;
- 重复 write 同一个未 flip 的 buffer → 实际写入 0 字节(因为 position == limit);
- read 后直接 write 而没 flip → 写入内容为空;
- 多线程共用同一 FileChannel + 同一 ByteBuffer → 竞态风险,应避免或加锁;建议每个操作使用独立 buffer 或严格同步 position 和 buffer 状态。
4. 一个简洁示例:读前 128 字节 → 修改第 64 字节 → 写回原位置
(使用 try-with-resources 确保关闭)
try (RandomAccessFile raf = new RandomAccessFile("data.bin", "rw");
FileChannel ch = raf.getChannel()) {
// ① 读前 128 字节
ByteBuffer buf = ByteBuffer.allocate(128);
ch.read(buf); // position=0→128
buf.flip(); // 准备读取 buffer 内容
// ② 修改第 64 字节(索引 63)
buf.put(63, (byte) 0xFF);
// ③ 写回原位置(position 需重置)
ch.position(0); // 回到开头
buf.rewind(); // position=0, limit=128,准备写
ch.write(buf); // 写入 128 字节
}

















