一、Buffer的定义
-
用于特定原始类型数据的容器。其已知直接子类有:
ByteBuffer
,CharBuffer
,DoubleBuffer
,FloatBuffer
,IntBuffer
,LongBuffer
,ShortBuffer
-
缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存。
-
Java NIO中的Buffer可用于和NIO通道进行交互。如你所知,数据是从通道读入缓冲区,从缓冲区写入到通道中的。// need an example
-
缓冲区是特定原始类型的元素的线性有限序列。
-
缓冲区不能安全地被多个并发线程使用。 如果一个缓冲区被多个线程使用,则应该通过适当的同步来控制对缓冲区的访问
二、基本属性
capacity
作为一个内存块,Buffer有一个固定的大小值,代表了缓冲区最大可写入的元素数量
position
下标,初始的position值为0,position表示当前的位置。相对读写(读写方法分为相对以及绝对两种类型,后面介绍)都从该位置开始。每次读写,position都会移动到下一个Buffer单元。position最大可为capacity – 1(下标从0开始,对应capacity个元素)
有多个方法都会对position重新赋值,最直接的方式
/**
* 对position进行赋值,如果新值小于mark,那么mark值将会被舍弃重新赋值为默认值-1
*/
public final Buffer position(int newPosition) {
if ((newPosition > limit) || (newPosition < 0))
throw new IllegalArgumentException();
position = newPosition;
if (mark > position) mark = -1;
return this;
}
当调用filp()方法将Buffer从写模式切换到读模式,position会被重置为0
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
limit
在写模式下,Buffer的limit表示你最多能往Buffer里写多少数据。 写模式下,limit始终等于Buffer的capacity。
当切换Buffer到读模式时, limit表示你最多能读到多少数据。因此,当切换Buffer到读模式时,limit会被设置成写模式下的position值。换句话说,你能读到之前写入的所有数据(limit被设置成已写数据的数量,这个值在写模式下就是position)
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
mark
标记,调用mark()函数会将mark设为当前的position值
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;
}
这几个变量之间存在关系 0 <= mark <= position <= limit <= capacity
三、Buffer中读模式和写模式的切换
写模式
从position位置开始(假如Buffer中已存在元素,则position不为0),最大限制为limit,此时limit等于capacity,每次写入一个数据后,position指针会跟着往下移动
写模式转换为读模式
调用flip()方法,从写模式切换到读模式时,position重置为0,从第一个元素开始开始读,最多可读元素个数limit等于读模式下的position值,即已写入的元素个数。
读模式切换为写模式
- 如果一次性读完了所有数据,使用clear()函数将position指针指向0,limit等于capacity
/**
* Clears this buffer. The position is set to zero, the limit is set to
* the capacity, and the mark is discarded.
*/
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
- 如果一次不读完所有数据,留下了一个空间没有读取,使用compact()函数将没有读取的数据拷贝到顶端去,limit指针指向第一个可写入元素的空位置,limit重新指向capacity
不会画图,相关文章参考https://blog.csdn.net/Funny_Ma/article/details/103534022
四、Buffer的分配
要想获得一个Buffer对象首先要进行分配。 每一个Buffer类都有一个allocate方法
ByteBuffer buf = ByteBuffer.allocate(48);
CharBuffer buf = CharBuffer.allocate(1024);
五、读写数据
- 所有Buffer子类都定义了两类get/put方法(可选择性实现),相对的get/put方法是从当前position进行操作,每次操作都会对position进行变更,而绝对的get/put方法需要参数指定下标,从指定位置进行操作,不会更改position。如ByteBuffer中的定义
/**
* Relative <i>put</i> method <i>(optional operation)</i>.
* 注意,这里是可选实现
* <p> Writes the given byte into this buffer at the current
* position, and then increments the position. </p>
*
*/
public abstract ByteBuffer put(byte b);
/**
* Absolute <i>get</i> method. Reads the byte at the given index.
*/
public abstract byte get(int index);
- 通过通道读入缓冲区,以及从缓冲区写入到通道中
int bytesRead = inChannel.read(buf); //read into buffer.
int bytesWritten = inChannel.write(buf); //write into buffer.
其他相关API在了解第二点中的基本属性后,查看源码很好理解,这里就不一一介绍了。