【Java】NIO Buffer 使用

797 阅读2分钟

这是我参与更文挑战的第25天,活动详情查看:更文挑战

一、前言

Buffer 可当作成缓冲区,或中转站,如图:

磁盘数据先经过 Buffer ,再到内存。

缓冲区(Buffer) :就是在内存中预留指定大小的存储空间用来对输入/输出(I/O)的数据作临时存储,这部分预留的内存空间就叫做缓冲区。

buffer1.png

一个Buffer对象是固定数量的数据的容器。

其属性有:

  1. 容量(Capacity): 缓冲区能够容纳的数据元素的最大数量。
  2. 上界(Limit): 缓冲区的第一个不能被读或写的元素, 用于计数。
  3. 位置(Position): 下一个要被读或写的元素的索引。
  4. 标记(Mark): 一个备忘位置; 当调用 reset() 方法,position 会回到 mark 指向的位置。

读写模式下, capacitylimitposition



二、API 使用

使用Buffer读写数据基本步骤:

  1. 写入数据到Buffer
  2. 调用flip() 方法
  3. Buffer中读取数据
  4. 调用clear()方法或者compact()方法
ByteBuffer buf = ByteBuffer.allocate(1024);

// 从 channel 中读取
int bytesRead = inChannel.read(buf);

while(bytesRead != -1) {
    buf.flip();
    
    while(buf.hasRemaining()) {
        System.out.println((char) buf.get());
    }
    
    buf.clear();
    bytesRead = inChannel.read(buf);
}

(1) Buffer 分配、读、写

directBufferbuffer 区别,如图:

buffer2.png

// 从磁盘文件 / 网络里 读取数据
ByteBuffer buf = ByteBuffer.allocate(1024);
ByteBuffer directBuf = ByteBuffer.allocateDirect(1024);
// 如果用 direct 模式,整体性能会比普通模式高一些。

如下 demo 演示 buffer 分配、读取、写入操作:

// 1. 分配 Buffer
ByteBuffer buffer = ByteBuffer.allocateDirect(100);

System.out.println("capacity : " + buffer.capacity());
System.out.println("position : " + buffer.position());
System.out.println("limit : " + buffer.limit());

byte[] src = new byte[] {1, 2, 3, 4, 5};

// 2. 放置数据,移动 position
buffer.put(src);

// position = 0~4,都填充了数据
System.out.println("position : " + buffer.position());

// 此时 position = 5,此时如果你直接读数据,是读不到的,是空的没有数据。
// 手动操作一下 position,调整到 position = 0的地方,开始读数据
buffer.position(0);

byte[] dst = new byte[5];

// 3. 将数据写到目标数组中
buffer.get(dst);
System.out.println("position : " + buffer.position());

输出如下:

capacity : 100
position : 0
limit : 100
position : 5
position : 5
dst=[0,1,2,3,4]

(2)通常调用 Buffer API 操作缓冲区

  1. clear() 方法

Buffer 被清空了。Buffer中的数据并未清除,只是这些标记告诉我们可以从哪里开始往Buffer里写数据。

position将被设回0,limit被设置成 capacity的值。

  1. flip() 方法

Buffer写模式 切换到 读模式

即将position设置为0, 并将limit设置成之前的position

  1. rewind() 方法

position 设置为0,并且丢弃 mark

一般先读取了一遍数据,接着想要再次重新读取一遍数据,这个时候可以用 rewind,此时 limit是不变的。