四、Java NIO 缓冲区

所有文章

https://www.cnblogs.com/lay2017/p/12901123.html

正文

本文继续Java NIO的缓冲区(Buffer)部分。我们前面不断提及的,Buffer将在和Channel打交道的时候使用。我们从Channel读入数据到Buffer,或者从Buffer写入数据到Channel。

那么Buffer是什么呢?Buffer其实就是一块指定了一定容量大小的内存空间,你可以对Buffer进行数据的读写。你也可以简单理解为定义了一个数组,这个数组被封装到了一个Buffer对象当中,并且提供了一些列的操作方法,使得易于使用。

Buffer的基本用法

使用Buffer来读写数据大体要遵循以下4个步骤

1.将数据写入到Buffer当中

2.调用Buffer的flip方法,翻转position到0的位置

3.将从position=0的位置开始从Buffer读取数据

4.调用Buffer的clear方法或者Buffer的compact方法,将已经读取完的数据进行清理

当你向一个Buffer读入数据,Buffer会持续跟踪你写入了多少数据。一旦你想要读取数据的时候,你需要将Buffer从写入模式切换到读取模式,因此需要先调用flip方法。

一旦你读取完了所有的数据,你需要清理Buffer,为再次写入做准备。你可以调用clear或者compact方法。clear方法将清空整个Buffer。compact方法只是清理了你已经读取的那部分数据。未读取的那部分,将被移到Buffer的开始位置,数据的写入将从未读取数据的最后一个位置的下一个位置开始。

下面是一个Buffer的使用示例,包含了write、flip、read、clear操作

// 构建文件对象
RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
// 获取通道
FileChannel inChannel = aFile.getChannel();
//分配一个48字节的缓冲区
ByteBuffer buf = ByteBuffer.allocate(48);
// 从通道读取数据到缓冲区
int bytesRead = inChannel.read(buf);
// 当数据不为空
while (bytesRead != -1) {
  // 翻转position=0,进入读取模式,从0开始读取数据
  buf.flip();
  // 当Buffer还有剩余数据的时候
  while(buf.hasRemaining()){
      // 读取并打印数据
      System.out.print((char) buf.get());
  }
  // 清空整个Buffer,准备写入
  buf.clear();
  // 再次从通道读取数据到Buffer
  bytesRead = inChannel.read(buf);
}
// 关闭文件对象
aFile.close();

Buffer的capacity、position、limit

一个Buffer是一个内存空间,你可以对它进行数据的读写。这块内存空间封装成了一个Buffer对象,并提供一些方便的操作方法使得它易于使用。

Buffer主要有三个属性你必须知道的:

1.capacity:容量

2.position:位置

3.limit:限制

position和limit的值取决于当前是读取模式还是写入模式。capacity总是从头到尾保持不变,无论是读取还是写入模式。

下图是两种模式写,这三个值对应的示例

 Capacity

作为一个内存空间,一个Buffer会有一个固定的大小,我们也叫做容量"capacity"。Buffer的最大空间是由capacity来确定的,如果你想要不断写入数据,你就需要不断clear调用Buffer中旧的数据,也就是为什么每次write到Buffer的时候要clear或者compact。

Position

当你想要对Buffer写入数据的时候,你是在一个确定的位置(position)上操作的,可以理解为数组的索引。在Buffer初始化的时候,position=0.当数据被写入到Buffer以后,position将会不断变化,指向下一个位置,不过position总是小于等于capacity-1

当你想要从Buffer读取数据的时候,也一样是从position的位置开始读取的,所以你需要调用flip将position=0。

Limit

在写入模式中,limit表示当前你可以写入Buffer的最大空间。limit总是等于capacity。

当你在读取模式的时候,调用了flip之后。在设置position=0之前会先把limit=position。这意味着你可以从position=0开始读取数据,直到limit前的数据。也就是说,之前写入了多少数据,这里就读取多少数据。

Buffer的类型

Java NIO的Buffer主要有以下几种类型

1.ByteBuffer

2.MappedByteBuffer

3.CharBuffer

4.DoubleBuffer

5.FloatBuffer

6.IntBuffer

7.LongBuffer

8.ShortBuffer

如你所见,以上的Buffer基本上就是对应了各种基本数据类型。MappedByteBuffer特殊一点,后续文章将会单独说明

创建一个Buffer

获取一个Buffer对象,其实就是分配一块内存空间。每一个Buffer实现类会有一个allocate方法来创建Buffer对象。

ByteBuffer buf = ByteBuffer.allocate(48);
CharBuffer buf = CharBuffer.allocate(1024);

写入数据到Buffer

写入数据到Buffer中有两种方式

1.从Channel写入数据到Buffer

int bytesRead = inChannel.read(buf);

2.调用put方法自己写入数据到Buffer

buf.put(127); 

flip

flip方法将Buffer从write模式切换到了read模式。调用flip方法position将设置为0,limit将设置为之前position所在的位置。

换句话说,position指定了从哪里开始读取数据,limit指定了你可以读取多少数据。

从Buffer读取数据

同样有两种方式从Buffer读取数据

1.从Buffer写入到Channel中

int bytesWritten = inChannel.write(buf);

2.从Buffer读取数据,通过get方法

byte aByte = buf.get();

rewind

rewind方法和flip方法类似,都是将position设置为0。但是rewind方法不影响limit的值,因此,如果你flip后读取了一次,想再读取一次。你可以使用rewind设置为0,重新读取。

clear和compact

一旦你从Buffer读取完了数据,你需要让Buffer可以再次写入。这时候你要调用clear或者compact方法。

如果你调用clear方法,position设置为0,limit设置为capacity。换句话说,Buffer是cleared状态,但是Buffer中的数据并没有清空。同时要注意,clear方法不管你是不是读取了Buffer中的所有数据,直接设置position=0,因此未读完的数据将被忽略。

如果你不想未读取完的数据被忽略,那么你需要调用compact方法,而不是clear方法。

compact方法会把未读取的数据拷贝到Buffer开始的位置,并设置position等于未读取完的数据的位置的下一个位置。并把limit=capacity,像clear一样。

调用了clear或者compact以后,Buffer将可以开始写入,Buffer中的数据将被覆盖。

mark和reset

如果你想标记position所在的位置,你需要调用mark方法。想要恢复position的位置到mark的位置,调用reset方法

buffer.mark();

buffer.reset();  //set position back to mark.    

equals和compareTo

你可能想要比较两个Buffer

equals比较相等

如果满足以下条件则相等

1.数据类型相等

2.剩余数据量相等

3.剩余数据相等

compareTo比较大小

如果满足以下条件表示小于

1.第一个元素的大小更小

2.元素更少

原文地址:https://www.cnblogs.com/lay2017/p/12905616.html