Java NIO中可随机覆盖写入但不可插入/删除,需用FileChannel.position()定位后write()等长覆盖;须以"rw"模式打开,注意文件长度边界及定长数据场景适用性。

Java NIO 中不能直接“随机修改”文件内容(比如在中间插入或删除字节),因为文件系统不支持原地变长/变短写入;但可以用 FileChannel 实现**随机位置的覆盖写入(overwrite)**,即定位到指定偏移量,写入等长的新数据。这是真正可行且常用的方式。
用 FileChannel 实现随机覆盖写入
核心是通过 position(long) 定位到目标字节偏移,再调用 write(ByteBuffer) 覆盖原有内容。注意:该操作不会改变文件长度,也不会自动移动后续数据。
- 必须以读写模式(
"rw")打开文件,例如:RandomAccessFile raf = new RandomAccessFile(file, "rw"); FileChannel channel = raf.getChannel(); - 调用
channel.position(offset)移动到指定字节位置(从 0 开始) - 准备好与要覆盖字节数等长的
ByteBuffer,并确保其limit()和position()设置正确(通常用ByteBuffer.wrap(bytes)或手动 flip) - 执行
channel.write(buffer)—— 此时只覆盖从 offset 开始的 buffer.capacity() 个字节
小心处理文件长度边界
如果试图写入的位置超出当前文件末尾,大多数操作系统会自动扩展文件(填充 \0 字节),但行为依赖底层系统。更安全的做法是预先确保文件足够长:
- 用
channel.size()获取当前长度 - 若
offset + buffer.remaining() > size,先调用channel.truncate(newSize)或channel.write(dummyBuf, newSize-1)扩容(例如写一个字节到末尾使文件变长) - 推荐方式:用
channel.position(size); channel.write(ByteBuffer.allocate(1));确保文件至少达到所需长度
避免覆盖破坏数据结构(如文本行、记录)
随机覆盖只适合定长格式场景,例如:
立即学习“Java免费学习笔记(深入)”;
- 二进制索引文件(每个条目固定 16 字节)
- 日志文件中更新某个字段(已知字段起始偏移和长度)
- 数据库页缓存模拟(页大小固定)
对变长文本(如 CSV、JSON)或需要增删内容的场景,不要用 FileChannel 直接覆盖——应读取全部/部分数据到内存,修改后整体重写文件,或改用内存映射(MappedByteBuffer)配合手动管理逻辑偏移。
可选:用 MappedByteBuffer 提升小范围频繁修改性能
对于需高频修改多个离散位置的小文件,MappedByteBuffer 更高效(直接操作内存,无内核态拷贝):
-
channel.map(FileChannel.MapMode.READ_WRITE, offset, length)映射一段区域 - 然后像操作普通 ByteBuffer 一样
put(int index, byte b)或put(byte[] src) - 修改后调用
force()确保刷盘(尤其重要!否则可能丢失) - 注意:映射长度不能超过文件实际大小,扩容仍需先调整文件长度


















