Java NIO 缓冲区
与NIO通道进行交互时,将使用Java NIO缓冲区。如您所知,数据从通道读取到缓冲区,然后从缓冲区写入通道。
缓冲区本质上是一个内存块,您可以在其中写入数据,然后可以在以后再次读取。该内存块包装在NIO Buffer对象中,该对象提供了一组方法,可以更轻松地使用该内存块。
1 Buffer的基本使用
使用Buffer来读取和写入数据通常遵循以下四个步骤:
- 将数据写入缓冲区
- 调用buffer.flip()方法
- 从缓冲区读取数据
- 调用buffer.clear()或buffer.compact()方法
当您将数据写入缓冲区时,缓冲区会跟踪您已写入多少数据。一旦需要读取数据,就需要使用flip()方法调用将缓冲区从写入模式切换到读取模式。在读取模式下,缓冲区使您可以读取写入缓冲区的所有数据。
读取所有数据后,需要清除缓冲区,以使其准备好再次写入。您可以通过两种方式执行此操作:通过调用clear()或通过 compact()。该clear()方法清除整个缓冲区。该compact() 方法仅清除您已经读取的数据。任何未读的数据都将移到缓冲区的开头,现在将在未读的数据之后将数据写入缓冲区。
以下一个简单的Buffer用法示例:
/**
* 小牛知识库网: https://www.xnip.cn
*/
RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
FileChannel inChannel = aFile.getChannel();
//create buffer with capacity of 48 bytes
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buf); //read into buffer.
while (bytesRead != -1) {
buf.flip(); //make buffer ready for read
while(buf.hasRemaining()){
System.out.print((char) buf.get()); // read 1 byte at a time
}
buf.clear(); //make buffer ready for writing
bytesRead = inChannel.read(buf);
}
aFile.close();
2 Buffer的容量、位置、限制
缓冲区本质上是一个内存块,您可以在其中写入数据,然后可以在以后再次读取。该内存块包装在NIO Buffer对象中,该对象提供了一组方法,可以更轻松地使用该内存块。
Buffer对象具有三个您需要熟悉的属性,以了解其Buffer工作原理。分别是:
- 容量(capacity)
- 位置(position)
- 限制(limit)
位置和限制取决于是否Buffer对象在读或写模式。不管缓冲模式如何,容量始终意味着相同。
下图说明写和读模式下的容量,位置和限制的说明。
写入和读取模式下的缓冲区容量,位置和限制。 |
2.1 容量
作为存储块,Buffer具有一定的固定大小,也称为其“容量”。您只能将capacity字节,long,char等写入缓冲区。缓冲区装满后,需要先清空(读取数据或清除数据),然后才能向其中写入更多数据。
2.2 位置
将数据写入Buffer时,您需要在特定位置进行。最初,该位置为0。将byte,long等数据写入Buffer该位置后,该位置将前进以指向缓冲区中的下一个要插入数据的单元。位置可以最大程度地变为 capacity - 1。
从a读取数据时,Buffer您也从指定位置读取数据。当您 Buffer从写入模式切换到读取模式时,该位置将重置为0。从中读取数据时,Buffer您将从中读取数据position,并position 前进到下一个读取位置。
2.3 限制
在写模式下,Buffer的限制是可以写入缓冲区的数据量的限制。在写入模式下,限制等于的容量Buffer。
当翻转Buffer到读模式,限制意味着多少数据可以从数据中读取的限制。因此,当将a切换Buffer到读取模式时,将限制设置为写入模式的写入位置。换句话说,您可以读取与写入的字节一样多的字节(限制设置为写入的字节数,该字节数由位置标记)。
3 Buffer的类型
Java NIO带有以下缓冲区类型:
- ByteBuffer
- MappedByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
如您所见,这些Buffer类型代表不同的数据类型。换句话说,它们使您可以将缓冲区中的字节作为char,short,int,long,float或double来使用。
4 Buffer的分配
要获取Buffer对象,您必须首先分配它。每个Buffer类都有一个allocate()执行此操作的方法。以下示例显示ByteBuffer了容量为48个字节的a的分配 :
ByteBuffer buf = ByteBuffer.allocate(48);
这是一个CharBuffer为1024个字符分配带空格的示例:
CharBuffer buf = CharBuffer.allocate(1024);
5 将数据写入Buffer
您可以通过Buffer两种方式将数据写入:
- 将数据从Channel写入Buffer
- Buffer通过缓冲区的put()方法将数据写入。
以下代码是Channel如何将数据写入Buffer的示例:
int bytesRead = inChannel.read(buf); //读入缓冲区。
以下是一个Buffer通过put()方法将数据写入的示例:
buf.put(127);
该put()方法还有许多其他版本,可让您以Buffer多种不同方式将数据写入 。例如,在特定位置写入或将字节数组写入缓冲区。有关更多详细信息,请参见JavaDoc以获取具体的缓冲区实现。
flip()方法
该flip()方法将Buffer从写入模式切换到读取模式。调用flip()会将positionback设置为0,并将设置为limit 刚才的位置。
换句话说,position现在标记读取位置,并limit标记将多少字节,字符等写入缓冲区-可以读取多少字节,字符等的限制。
6 从Buffer读取数据
您可以通过两种方式从读取数据Buffer。
- 从缓冲区读取数据到通道Channel。
- 使用get() 方法从缓冲区读取数据。
以下是如何从缓冲区将数据读取到通道的示例:
//从缓冲区读取到通道。
int bytesWritten = inChannel.write(buf);
以下是Buffer使用get()方法读取数据的示例:
byte aByte = buf.get();
get()方法还有许多其他版本,可让您以Buffer多种不同方式从中读取数据 。例如,在特定位置读取或从缓冲区读取字节数组。有关更多详细信息,请参见JavaDoc以获取具体的缓冲区实现。
rewind()
该Buffer.rewind()套position回0,这样你就可以重新读取缓冲区中的所有数据。在limit保持不变,因此仍标记,可以从被读取许多元素(字节,字符等)如何Buffer。