《Java NIO》学习笔记 —— 缓冲区
一、缓冲区基础
1、缓冲区的基础
在 NIO 中,Buffer 是数据读写的核心。它是一个固定容量的用于承载数据的容器,用于在通道(Channel)和程序之间存放数据。
通俗来说:
通道负责“运输”,缓冲区负责“存放”。
数据总是先进入 Buffer,再从中读出。
2、Buffer的四大属性
| 属性 | 含义 | 可修改 | 说明 |
|---|---|---|---|
| capacity | 容量 | ❌ | 缓冲区可容纳的最大元素数 |
| limit | 上界 | ✅ | 第一个不能被读写的位置 |
| position | 当前位置 | ✅ | 下一个读或写操作的索引 |
| mark | 标记 | ✅ | 记录一个position,可通过 reset 回到这里 |
属性之间总是遵循的关系:0 <= mark <= position <= limit <= capacity
3、缓冲区API
缓冲区的各类操作大部分是围绕Buffer的四大属性来进行的,先看Buffer类的方法签名:
public abstract class Buffer {
public final int capacity( )
public final int position( )
public final Buffer position (int newPositio)
public final int limit( )
public final Buffer limit (int newLimit)
public final Buffer mark( )
public final Buffer reset( )
public final Buffer clear( )
public final Buffer flip( )
public final Buffer rewind( )
public final int remaining( )
public final boolean hasRemaining( )
public abstract boolean isReadOnly( );
}
细心的同学应该可以看到很多方法返回值都是Buffer引用(this),这意味着可以链式(级联)调用,比如:
buffer.mark().position(5).reset( );
下面的如涉及具体操作都将以CharBuffer为例,其它的都大差不差。
3.1 存取 (get/put)
Buffer 的核心作用是存储数据。通过 put() 写入数据,通过 get() 读取数据。它支持相对操作和绝对操作两种方式:
| 操作类型 | 方法示例 | 特点 |
|---|---|---|
| 相对 | buffer.put(byte) / buffer.get() | 自动移动 position |
| 绝对 | buffer.put(int index, byte) / buffer.get(int index) | 不影响 position |
异常处理
| 异常 | 触发条件 |
|---|---|
BufferOverflowException | 写超出 limit |
BufferUnderflowException | 读超出 limit |
IndexOutOfBoundsException | 索引越界 |
3.2 翻转 (flip)
flip() 是最常用的状态切换操作,可以理解为将 写模式 转为 读模式,将进行以下操作:
limit = position
position = 0
这样通道或程序就可以从头开始读取刚写入的数据
但是:重复 flip() 会导致 limit=position=0,此时缓冲区为空,读取会抛异常
3.3 释放 (drain)
“释放”指从缓冲区中取出数据,通常配合通道读取,常见于:
通道写入:
channel.write(buffer)手动取出:循环
get()
······示例
buffer.flip(); // 进入读模式
while (buffer.hasRemaining()) {
byte b = buffer.get();
System.out.print((char) b);
}
3.4 压缩 (compact)
当缓冲区只读取了部分数据,compact() 可以将未读数据前移,以便继续写入新数据。
未读部分移到开头
position = 未读数据长度
limit = capacity
图解示意:

3.5 标记 (mark/reset)
提供在缓冲区中回退到某个位置的能力。
| 方法 | 功能 |
|---|---|
mark() | 记录当前位置 |
reset() | 回到标记位置 |
注意:标记会在以下操作中被清除:
clear()
flip()
rewind()修改 limit 小于 mark 时
3.6 比较 (equals/compare To)
比较缓冲区内容时,只比较剩余部分(position→limit)。
| 方法 | 含义 |
|---|---|
boolean equals() | 内容完全相同 |
int compareTo() | 更加严格,按字典序比较 |
两个缓冲区相等的充要条件:
- 两个对象类型相同
- 两个对象都剩余同样数量的元素(Buffer的容量不需要相同,而且缓冲区中剩余数据的索引也不必相同)
- 在每个缓冲区中应被Get()函数返回的剩余数据元素序列必须一致
3.7 批量移动(bulk operations)
相比逐个 get/put,批量移动能显著提高效率。
| 方向 | 方法 |
|---|---|
| 数组 → 缓冲区 |
put (char[] src) put (char [] src, int offset, int length) |
| 缓冲区 → 数组 |
get (char [] dst) get (char [] dst, int offset, int length) |
| 缓冲区 → 缓冲区 | put (otherBuffer) |
提示:
- 将缓冲区内的数据放入数组时,批量传输的大小总是固定的。省略长度意味着整个数组会被填满;因此当传入一个数组并且没有指定长度,就相当于要求整个数组被填充。如果缓冲区中的数据不够完全填满数组,您会得到一个BufferUnderflowException异常。
- 同样的,在将数据装入缓冲区时,如果缓冲区中没有足够的空间,那么不会有数据被传递,同时抛出一个BufferOverflowException异常。
数据读取并处理实例:
char [] smallArray = new char [10];
while (buffer.hasRemaining()) {
int length = Math.min (buffer.remaining(), smallArray.length);
buffer.get (smallArray, 0, length);
processData (smallArray, length);
}
4、常见的状态变化
| 方法 | 作用 | 将会进行的操作 |
|---|---|---|
| clear() | 准备写入数据 | position=0, limit=capacity |
| flip() | 从写模式切到读模式 | limit=position, position=0 |
| rewind() | 重头读 | position=0,不改 limit |
| compact() | 丢弃已读数据,保留未读 | position=未读数据末尾 |
| mark()/reset() | 标记/回到标记 | 用于重复读取 |
免责声明
本文内容整理自《Java NIO 中文版》及个人学习笔记,仅供学习与交流使用。文中示例及观点不代表官方立场,如有错误或侵权请联系作者及时更正或删除。转载请注明出处。
更多推荐


所有评论(0)