概述
Buffer
类是构建 Java NIO
的基础。在这些类中,ByteBuffer
类是最受欢迎的。因为字节
类型是最通用的类型。例如,可以在 JVM
中使用字节来组成其他非布尔基元类型。另外,还可以使用字节在 JVM
和外部I/O
设备之间传输数据。
ByteBuffer创建
ByteBuffer
的创建有两种方法,一种是分配,一种是包装已有的byte
数组
分配
分配将创建一个实例并分配具有特定容量的私有空间。ByteBuffer
*类有两种分配方法:allocate
*和allocateDirect
。
使用allocate
方法,我们将获得一个非直接缓冲区 - 即具有底层字节数组的缓冲区实例:
1 | ByteBuffer buffer=ByteBuffer.allocate(10); |
当我们使用allocateDirect方法时,它将生成一个直接缓冲区:
1 | ByteBuffer buffer=ByteBuffer.allocateDirect(10); |
包装
包装允许实例重用现有的字节数组:
1 | byte[]bytes=new byte[10]; |
而上面的代码相当于:
1 | ByteBuffer buffer=ByteBuffer.wrap(bytes,0,bytes.length);复制 |
对现有字节数组中的数据元素所做的任何更改都将反映在缓冲区实例中,反之亦然。
ByteBuffer方法
ByteBuffer实际上是包装在对象中的字节数组,根据这个可以将方法划分为四类:
- Basic
- Mark and Reset
- Clear, Flip, Rewind, and Compact
- Remain
四个基本方法
ByteBuffer
依靠四个变量记录了数据状态:
- Capacity:缓冲区支持的最大容量
- Limit:用于停止读或写的下标索引
- Position:当前读或写的下标位置
- Mark:一个记住的下标位置
这些变量存在不变的关系:
1 | 0<=mark<=position<=limit<=capacity |
Mark and Reset
mark ()
和reset()
方法使我们能够记住特定位置并稍后返回该位置。
当我们第一次创建ByteBuffer实例时,标记是未定义的。然后,我们可以调用mark()
方法,并将标记设置为当前位置。经过一些操作后,调用reset()
方法会将位置更改回标记。
1 | ByteBuffer buffer=ByteBuffer.allocate(10); // mark = -1, position = 0 |
Clear, Flip, Rewind, and Compact
clear()
, flip()
, rewind()
, and compact()
四个方法功能功能如下:
操作示例:
1 | ByteBuffer buffer=ByteBuffer.allocate(10); // mark = -1, position = 0, limit = 10 |
在上面的代码块执行后分别执行下面四个方法,会有不同的效果:
clear()
方法会将Capacity赋值给Limit,Position赋值为0,Mark赋值为-1。
1 | buffer.clear(); // mark = -1, position = 0, limit = 10 |
flip()
方法会先将Position赋值给Limit,之后Position赋值为0,Mark赋值为-1。
1 | buffer.flip(); // mark = -1, position = 0, limit = 5 |
remind()
方法会不动Limit,将Position赋值为0,Mark赋值为-1。
1 | buffer.rewind(); // mark = -1, position = 0, limit = 8 |
compact()
方法将(limit-position)赋值给position,Capacity赋值给limit,,mark赋值为-1。
1 | buffer.compact(); // mark = -1, position = 3, limit = 10 |
这四个方法的用处:
- clear:重用缓冲区。
- flip:缓冲区从写模式切换到读模式。
- rewind:多次读取底层数据。
- compact:缓冲区的部分重用,当你从缓冲区中读取一部分数据后,原始的缓冲区可能会包含已读数据和未读数据。compact() 方法会将未读的数据复制到缓冲区的起始位置,从而释放已读数据所占用的空间