前置知识: Java、Java

Java NIO 通道与缓冲区

2 min入门

Java NIO 通道与缓冲区 的完整教学讲解。

0. 本节阅读指引(先读这一节)

本篇是「NIO 通道与缓冲区」语法速查手册,按需查阅。

零基础第一遍只读:Buffer 创建、Buffer 读写操作、Buffer 状态属性、FileChannel;Selector、Scatter/Gather、Charset、Path、异步文件通道遇到再查。

前置:026 I/O 流与文件操作、059 Java IO 与 NIO。

Buffer 创建

基本写法:创建字节缓冲区 ByteBuffer.allocate(<容量>);

// 分配堆内字节缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024);

基本写法:创建直接缓冲区 ByteBuffer.allocateDirect(<容量>);

// 分配堆外直接缓冲区(减少拷贝)
ByteBuffer direct = ByteBuffer.allocateDirect(1024);

基本写法:包装数组 ByteBuffer.wrap(<字节数组>);

// 包装现有数组为 Buffer
ByteBuffer buf = ByteBuffer.wrap(new byte[]{1, 2, 3});

Buffer 读写操作

基本写法:写入数据 <buffer>.put(<值>);

// 向缓冲区写入字节
buf.put((byte) 65);

基本写法:读取数据 <buffer>.get();

// 从缓冲区读取字节
byte b = buf.get();

基本写法:切换为读模式 <buffer>.flip();

// 写完后翻转为读模式
buf.flip();

基本写法:重置位置 <buffer>.rewind();

// 重置 position 以便重新读
buf.rewind();

基本写法:清空缓冲区 <buffer>.clear();

// 清空缓冲区准备再次写入
buf.clear();

基本写法:压缩缓冲区 <buffer>.compact();

// 压缩未读数据到头部
buf.compact();

Buffer 状态属性

基本写法:获取容量 <buffer>.capacity();

// 获取缓冲区容量
int cap = buf.capacity();

基本写法:获取位置 <buffer>.position();

// 获取当前位置
int pos = buf.position();

基本写法:获取限制 <buffer>.limit();

// 获取限制位置
int limit = buf.limit();

基本写法:获取剩余量 <buffer>.remaining();

// 获取剩余可读元素数量
int rem = buf.remaining();

FileChannel 文件通道

基本写法:从文件获取通道 FileChannel.open(<路径>, <打开选项>...);

// 打开文件通道
FileChannel ch = FileChannel.open(Path.of("a.txt"), StandardOpenOption.READ);

基本写法:通道读入缓冲区 <channel>.read(<buffer>);

// 从通道读取到 Buffer
int n = ch.read(buf);

基本写法:缓冲区写入通道 <channel>.write(<buffer>);

// 将 Buffer 数据写入通道
ch.write(buf);

基本写法:传输文件 <channel>.transferTo(<位置>, <数量>, <目标通道>);

// 零拷贝传输文件内容
src.transferTo(0, src.size(), dst);

Scatter / Gather

基本写法:分散读 <channel>.read(<buffer数组>);

// 一次读入多个 Buffer
ByteBuffer[] bufs = {header, body};
channel.read(bufs);

基本写法:聚集写 <channel>.write(<buffer数组>);

// 多个 Buffer 一次写出
ByteBuffer[] bufs = {header, body};
channel.write(bufs);

Selector 选择器

基本写法:创建选择器 Selector.open();

// 打开选择器
Selector selector = Selector.open();

基本写法:注册通道到选择器 <channel>.register(<selector>, <就绪事件>);

// 注册通道为可读事件
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_READ);

基本写法:选择就绪通道 <selector>.select();

// 阻塞直到有就绪通道
int ready = selector.select();

基本写法:获取就绪键 <selector>.selectedKeys();

// 获取就绪的 SelectionKey 集合
Set<SelectionKey> keys = selector.selectedKeys();

Charset 字符编码

基本写法:编码字符串到字节 <charset>.encode(<字符串>);

// 使用 UTF-8 编码
ByteBuffer b = StandardCharsets.UTF_8.encode("hello");

基本写法:解码字节到字符串 <charset>.decode(<buffer>);

// 使用 UTF-8 解码
String s = StandardCharsets.UTF_8.decode(buf).toString();

Path 路径操作

基本写法:创建路径 Path.of("<路径>");

// 创建 Path 对象
Path p = Path.of("a", "b", "c.txt");

基本写法:读取文件所有字节 Files.readAllBytes(<路径>);

// 一次性读取小文件全部字节
byte[] all = Files.readAllBytes(Path.of("a.txt"));

基本写法:写入文件 Files.write(<路径>, <字节数组>);

// 写入字节数组到文件
Files.write(Path.of("out.txt"), bytes);

基本写法:按行读取 Files.readAllLines(<路径>);

// 按行读取文件
List<String> lines = Files.readAllLines(Path.of("a.txt"));

异步文件通道

基本写法:打开异步文件通道 AsynchronousFileChannel.open(<路径>, <选项>...);

// 打开异步文件通道
AsynchronousFileChannel ch = AsynchronousFileChannel.open(
    Path.of("a.txt"), StandardOpenOption.READ);

基本写法:异步读取 <channel>.read(<buffer>, <位置>, <附件>, <完成处理器>);

// 异步读取并回调
ch.read(buf, 0, null, new CompletionHandler<Integer, Object>() {
    public void completed(Integer n, Object att) { }
    public void failed(Throwable e, Object att) { }
});