404. Java 文件操作基础 - 使用 Channel I/O 读写文件
1️⃣ 背景介绍
- Stream I/O:一次处理一个字符(或字节),适合顺序读取或写入。
- Channel I/O:一次处理一个 缓冲区(Buffer),可以更高效地进行读写,尤其适合大文件。
ByteChannel提供基本的读写方法。SeekableByteChannel是ByteChannel的扩展,支持:- 随机访问文件(移动到任意位置读写)
- 文件截断
- 查询文件大小
- 支持
READ、WRITE、APPEND等操作
Channel I/O 特别适合需要随机访问或高性能 I/O 的场景。
2️⃣ 常用方法
| 方法 | 功能 |
|---|---|
Files.newByteChannel(Path, OpenOption...) | 打开或创建文件,返回 SeekableByteChannel |
Files.newByteChannel(Path, Set<? extends OpenOption>, FileAttribute<?>...) | 可以指定一组 OpenOption 和文件属性 |
默认返回
SeekableByteChannel,在默认文件系统中可以直接 cast 成 FileChannel,从而使用更多高级功能:
- 文件映射(
memory-mapped I/O)- 文件锁定(防止其他进程访问)
- 从任意位置读写数据而不影响当前位置
3️⃣ 示例 1:读取文件并打印内容
public static void readFile(Path path) throws IOException {
// 默认以 READ 模式打开
try (SeekableByteChannel sbc = Files.newByteChannel(path)) {
final int BUFFER_CAPACITY = 10; // 每次读取 10 个字节
ByteBuffer buf = ByteBuffer.allocate(BUFFER_CAPACITY);
String encoding = System.getProperty("file.encoding"); // 平台默认编码
while (sbc.read(buf) > 0) {
buf.flip(); // 切换为读取模式
System.out.print(Charset.forName(encoding).decode(buf));
buf.clear(); // 清空缓冲区以便下一轮写入
}
}
}
✅ 讲解要点:
- 使用 ByteBuffer 来读取字节块,提高效率
buf.flip()→ 切换缓冲区为读取模式buf.clear()→ 清空缓冲区,为下一次读取准备- 默认使用平台编码解码字符,避免乱码
4️⃣ 示例 2:写入带权限的日志文件(POSIX 文件系统)
import static java.nio.file.StandardOpenOption.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.io.*;
import java.util.*;
public class LogFilePermissionsTest {
public static void main(String[] args) {
// 设置写入选项:追加 + 创建
Set<OpenOption> options = new HashSet<>();
options.add(APPEND);
options.add(CREATE);
// 设置文件权限:owner rw, group r, others 无权限
Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-r-----");
FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perms);
// 准备写入的字节数据
String s = "Hello World! ";
ByteBuffer bb = ByteBuffer.wrap(s.getBytes());
Path file = Paths.get("./permissions.log");
try (SeekableByteChannel sbc = Files.newByteChannel(file, options, attr)) {
sbc.write(bb);
System.out.println("写入完成: " + s);
} catch (IOException x) {
System.err.println("写入异常: " + x);
}
}
}
✅ 讲解要点:
Files.newByteChannel()可同时指定 写入选项 + 文件属性PosixFilePermissions.fromString("rw-r-----")→ 设置 Unix 风格文件权限- 使用
ByteBuffer.wrap()将字节数组封装为缓冲区 APPEND+CREATE→ 文件不存在则创建,存在则追加内容
5️⃣ 总结
✅ Channel I/O 优势
- 批量处理数据,提高效率
- 支持随机访问(SeekableByteChannel)
- 支持高级功能(FileChannel):
- Memory-mapped I/O
- 文件锁定
- 从任意位置读写数据
✅ 使用技巧
flip()与clear()是 ByteBuffer 操作关键- try-with-resources 保证资源释放
- POSIX 文件权限可以在创建文件时直接设置
🔹 建议练习
- 使用 Channel I/O 读取大文件并统计行数
- 写一个随机写入示例:在文件中间插入内容
- 在不同操作系统上验证 POSIX 权限设置的效果