继承体系如下:

Buffer虽然是基类,但并没有定义数据的容器,仅仅定义了几个用来做标志的变量。
private int mark = -1;
private int position = 0;
private int limit;
private int capacity;
先说下面几个吧
capacity表示容器得大小,在ByteBuffer中对应数组的大小,一般需要人为指定,通常取值为1024或者1024的倍数。
position表示当前所在的地方
limit表示读写的限制。在写模式下,等于capacity,也就是最大容量;在读模式,等于buffer容器内现有数据的长度。
网上找个图过来(从上往下看,最上面是起点)

上面的图左右对照起来看,正是描述了从写模式切换到读模式的过程:将limit放到position所在的位置(表示最多能读取多少数据),再将postion置为0,从头开始读取刚才写入的数据。
对应于Buffer类中的flip()方法
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
其实在使用Buffer类时,我们通常会组合调用一些方法。
1、向Buffer内写数据
写操作时,我们通常会调用clear()方法清空容器。(这里的read是相对于流来说的,对于Buffer本身来说,是往里面放东西,是一个write的过程)
buf.clear(); // Prepare buffer for reading
in.read(buf); // Read data
clear()方法仅仅通过修改标志来实现清空。
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
2、向Buffer内 写-读 数据
一般会先设置头信息,再写入主题信息,写操作做完后,切换为读模式
buf.put(magic); // Prepend header
in.read(buf); // Read data into rest of buffer
buf.flip(); // Flip buffer
out.write(buf); // Write header + data to channel
3、倒带 rewind
适用于”读完一次不过瘾,还想再读一次“
out.write(buf); // Write remaining data
buf.rewind(); // Rewind buffer
buf.get(array); // Copy data into array
rewind()将postion重新置为0
get()方法将buf内的数据再取出来一次,并放入数组。
rewind的主要逻辑如下
public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}
4、mark reset
正如其字面含义,mark是用来做标记的,调用mark方法,标记当前position所在的位置。在必要的时候,调用reset方法,可以重新回到之前标志的地方。
public final Buffer mark() {
mark = position;
return this;
}
reset()
public final Buffer reset() {
int m = mark;
if (m < 0)
throw new InvalidMarkException();
position = m;
return this;
}
5、compact方法
网上有很多博客对其描述都有点问题。
我需要读取 A B C D E F 这几个数,此时我读取到了B,还剩下CDEF没有读,调用compact方法后,将CDEF拷贝数组的最前端,并且让postion指向F。
为啥执行F而不是指向0呢?
主要是为了解决这个场景下遇到的问题:在进行读操作时,突然不想读了,我想进行写操作,等写完剩余的数据后,我再flip一下,从头开始读取。
这种情况下,就可以使用compact方法,将剩下没来得及读取的数copy到数组的首部,并把position置为剩余数据尾部,注意是剩余数据尾部,而不是头部。
看一下源码的实现:
public ByteBuffer compact() {
System.arraycopy(hb, ix(position()), hb, ix(0), remaining());
position(remaining());
limit(capacity());
discardMark();
return this;
}
ix是给他加一个偏移量,可以先不考虑。整个的逻辑就是将剩余未读的地方拷贝到数组的最前面,并把positon移动到数据的末尾。
配合之前写的一篇demo食用更佳:java nio简单示例:实现客户端与服务器一问一答
参考:
http://ifeve.com/buffers/#clearandcompact
本文详细解析了Java NIO中的Buffer类,介绍了其关键属性如capacity、position、limit以及mark、reset、compact等方法的作用,通过实例展示了如何在读写模式切换和数据处理中灵活运用。

365

被折叠的 条评论
为什么被折叠?



