缓冲区(Buffers)本质上是一个可以写入数据的内存块,然后可以读取。 这个内存块包含在NIO Buffer对象中,该对象提供了一组api,这样可以方便用户更轻松地使用内存块。
在NIO中已实现的buffer类中继承Buffer这个抽象类
public abstract class Buffer {
// Cached unsafe-access object
static final Unsafe UNSAFE = Unsafe.getUnsafe();
/**
* The characteristics of Spliterators that traverse and split elements
* maintained in Buffers.
*/
static final int SPLITERATOR_CHARACTERISTICS =
Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED;
// Invariants: mark <= position <= limit <= capacity
private int mark = -1;
private int position = 0;
private int limit;
private int capacity;
//略。。。
}
其他具体的实现方式,根据不同的需求实现例如 ByteBuffer底层使用的为byte数组 CharBuffer使用char数组 DirectByteBuffer使用直接内存,这里通过从抽象类中的Unsafe 类分配直接内存使用。
当数据写入缓冲区时,缓冲区会跟踪写入的数据量(在buffer对象中有position 会跟踪写入点)。 一旦需要读取数据,就需要使用flip() 方法调用将缓冲区从写入模式切换到读模式。 在读模式下,缓冲区允许读取已经写入缓冲区的所有数据。一旦读完所有数据,需要清除缓冲区,以便再次写入。 可以通过两种方式执行:调用clear()或调用compact() 方法。 clear() 方法清除整个缓冲区。 compact()方法仅清除已读取的数据。 任何还没读的数据都会移动到缓冲区的开头,接下来写入时,就会在这些未读数据之后写入。 下面用一个简单的例子来说明
public class ReadFile {
public static void main(String[] args){
readFile();
}
public static void readFile(){
try(RandomAccessFile randomAccessFile =
new RandomAccessFile("F:\\data\\test.txt","rw")){
//获取连接通道
FileChannel fileChannel = randomAccessFile.getChannel();
//初始化一个缓冲区,大小为1024byte
ByteBuffer buf = ByteBuffer.allocate(1024);
//fileChannel.read(buf)从通道中读取文件的数据放入缓冲区,返回结构为读取的字节数
//注意:这个方法会记录读取的位置,所以后续读取不会从头开始读取
int bytesRead = fileChannel.read(buf);
System.out.println(bytesRead);
//读取到数据
while(bytesRead != -1)
{
//反转后为可以读取缓冲中的数据
buf.flip();
while(buf.hasRemaining())//position < limit;表示可读
{
//读取
System.out.print((char)buf.get());
}
//没有可读数据后,清除已读数据,从未读数据之后开始写入
buf.compact();
//开始继续读取
bytesRead = fileChannel.read(buf);
}
}catch (IOException e){
e.printStackTrace();
}
}
}
如下图分别表示写入和读取操作的buffer
图片.png
关于Buffer中方法下一节详解