Java NIO Buffer 缓冲区


Buffer 类(缓冲区)

  • Buffer 是一个对象,它对某种基本类型的数组进行了封装。NIO 开始使用的 Channel(通道)就是通过 Buffer 来读写数据的;
  • 在 NIO 中,所有的数据都是用 Buffer 处理的,它是 NIO 读写数据的中转池。Buffer 实质上是一个数组,通常是一个字节数据,但也可以是其他类型的数组。但一个缓冲区不仅仅是一个数组,重要的是它提供了对数据的结构化访问,而且还可以跟踪系统的读写进程;
  • 使用 Buffer 读写数据一般遵循以下四个步骤:
    1.写入数据到 Buffer;
    2.调用 flip() 方法;
    3.从 Buffer 中读取数据;
    4.调用 clear() 方法或者 compact() 方法。
  • 当向 Buffer 写入数据时,Buffer 会记录下写了多少数据。一旦要读取数据,需要通过 flip() 方法将 Buffer 从写模式切换到读模式。在读模式下,可以读取之前写入到 Buffer 的所有数据;
  • 一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。有两种方式能清空缓冲区:调用 clear() 或 compact() 方法。clear() 方法会清空整个缓冲区。compact() 方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面;
  • Buffer主要有如下几种:
    • ByteBuffer
    • CharBuffer
    • DoubleBuffer
    • FloatBuffer
    • IntBuffer
    • LongBuffer
    • ShortBuffer

1. 创建 ByteBuffer

  • ByteBuffer 类内部封装了一个 byte[] 数组,并可以通过一些方法对这个数组进行操作;
  • 创建 ByteBuffer 对象:
    • 方式一:在堆中创建缓冲区:allocate(int capacity),例如 ByteBuffer byteBuffer = ByteBuffer.allocate(10);,在堆中创建缓冲区称为:间接缓冲区;在这里插入图片描述
    • 方式二:在系统内存创建缓冲区:allocatDirect(int capacity),例如 ByteBuffer byteBuffer = ByteBuffer.allocateDirect(10);,在系统内存创建缓冲区称为:直接缓冲区,间接缓冲区的创建和销毁效率要高于直接缓冲区,间接缓冲区的工作效率要低于直接缓冲区;
    • 方式三:通过数组创建缓冲区:wrap(byte[] arr),例如 byte[] byteArray = new byte[10]; ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);,通过数组创建的缓冲区为:间接缓冲区;

2. 向 ByteBuffer 添加数据

  • 向当前可用位置添加数据:public ByteBuffer put(byte b)
import java.nio.ByteBuffer;
import java.util.Arrays;

public class Test {
    public static void main(String[] args) {
        ByteBuffer buf = ByteBuffer.allocate(10);
        buf.put((byte) 10);
        buf.put((byte) 20);
        System.out.println(Arrays.toString(buf.array()));
    }
}
/*
输出
[10, 20, 0, 0, 0, 0, 0, 0, 0, 0]
 */

在这里插入图片描述

  • 向当前可用位置添加一个 byte[] 数组:public ByteBuffer put(byte[] byteArray)
import java.nio.ByteBuffer;
import java.util.Arrays;

public class Test {
    public static void main(String[] args) {
        ByteBuffer buf = ByteBuffer.allocate(10);
        buf.put((byte) 10);
        buf.put((byte) 20);
        byte[] byteArray = {30, 40, 50};
        buf.put(byteArray);//添加整个数组
        System.out.println(Arrays.toString(buf.array()));
    }
}
/*
输出
[10, 20, 30, 40, 50, 0, 0, 0, 0, 0]
 */

在这里插入图片描述

  • 添加一个 byte[] 数组的一部分:public ByteBuffer put(byte[] byteArray,int offset,int len)
import java.nio.ByteBuffer;
import java.util.Arrays;

public class Test {
    public static void main(String[] args) {
        ByteBuffer buf = ByteBuffer.allocate(10);
        buf.put((byte) 10);
        buf.put((byte) 20);
        byte[] byteArray = {30, 40, 50};
        buf.put(byteArray,0,2);//只添加byteArray的前两个元素
        System.out.println(Arrays.toString(buf.array()));
    }
}
/*
输出
[10, 20, 30, 40, 0, 0, 0, 0, 0, 0]
 */

3. 容量 capacity

  • Buffer 的容量(capacity)是指:Buffer 所能够包含的元素的最大数量。定义了 Buffer 后,容量是不可变的;
import java.nio.ByteBuffer;

public class Test {
    public static void main(String[] args) {
        ByteBuffer b1 = ByteBuffer.allocate(10);
        System.out.println("容量:" + b1.capacity());//10。之后不可改变
        byte[] byteArray = {97, 98, 99, 100};
        ByteBuffer b2 = ByteBuffer.wrap(byteArray);
        System.out.println("容量:" + b2.capacity());//4。之后不可改变
    }
}
/*
输出
容量:10
容量:4
 */

4. 限制 limit

  • 限制 limit 是指:第一个不应该读取或写入元素的 index 索引。缓冲区的限制(limit)不能为负,并且不能大于容量;
  • 有两个相关方法:
    • 获取此缓冲区的限制:public int limit()
    • 设置此缓冲区的限制:public Buffer limit(int newLimit)
import java.nio.ByteBuffer;

public class Test {
    public static void main(String[] args) {
        ByteBuffer buf = ByteBuffer.allocate(10);
        System.out.println("初始容量:" + buf.capacity() +
                " 初始限制:" + buf.limit());//10
        buf.limit(3);//设置限制为:索引3
        buf.put((byte) 10);//索引:0
        buf.put((byte) 20);//索引:1
        buf.put((byte) 30);//索引:2
        buf.put((byte) 40);//抛出异常
    }
}
/*
输出
初始容量:10 初始限制:10
Exception in thread "main" java.nio.BufferOverflowException
	at java.base/java.nio.Buffer.nextPutIndex(Buffer.java:696)
	at java.base/java.nio.HeapByteBuffer.put(HeapByteBuffer.java:204)
	at Test.main(Test.java:15)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:567)
	at com.intellij.rt.execution.application.AppMainV2.main(AppMainV2.java:131)
 */

在这里插入图片描述

5. 位置 position

  • 位置 position 是指:当前可写入的索引。位置不能小于 0,并且不能大于"限制";
  • 有两个相关方法:
    • 获取当前可写入位置索引:public int position()
    • 更改当前可写入位置索引:public Buffer position(int p)
import java.nio.ByteBuffer;
import java.util.Arrays;

public class Test {
    public static void main(String[] args) {
        ByteBuffer buf = ByteBuffer.allocate(10);
        System.out.println("初始容量:" + buf.capacity() +
                " 初始限制:" + buf.limit() +
                " 当前位置:" + buf.position());//0
        buf.put((byte) 10);//position = 1
        buf.put((byte) 20);//position = 2
        buf.put((byte) 30);//position = 3
        System.out.println("当前容量:" + buf.capacity() +
                " 初始限制:" + buf.limit() +
                " 当前位置:" + buf.position());//3

        buf.position(1);//当position改为:1
        buf.put((byte) 2);//添加到索引:1
        buf.put((byte) 3);//添加到索引:2
        System.out.println(Arrays.toString(buf.array()));
    }
}
/*
输出
初始容量:10 初始限制:10 当前位置:0
当前容量:10 初始限制:10 当前位置:3
[10, 2, 3, 0, 0, 0, 0, 0, 0, 0]
 */

6. 标记 mark

  • 标记 mark 是指:当调用缓冲区的 reset() 方法时,会将缓冲区的 position 位置重置为该索引。不能为 0,不能大于 position;
  • 相关方法:
    • 设置此缓冲区的标记为当前的 position 位置:public Buffer mark()
import java.nio.ByteBuffer;
import java.util.Arrays;

public class Test {
    public static void main(String[] args) {
        ByteBuffer buf = ByteBuffer.allocate(10);
        System.out.println("初始容量:" + buf.capacity() +
                " 初始限制:" + buf.limit() +
                " 当前位置:" + buf.position());//初始标记不确定
        buf.put((byte) 10);
        buf.put((byte) 20);
        buf.put((byte) 30);
        System.out.println("当前容量:" + buf.capacity() +
                " 当前限制:" + buf.limit() +
                " 当前位置:" + buf.position());
        buf.position(1);//当position改为:1
        buf.mark();//设置索引1位标记点
        buf.put((byte) 2);//添加到索引:1
        buf.put((byte) 3);//添加到索引:2
        //当前position为:3
        //将position设置到之前的标记位:1
        buf.reset();
        System.out.println("reset后的当前位置:" + buf.position());
        buf.put((byte) 20);//添加到索引:1
        System.out.println(Arrays.toString(buf.array()));
    }
}
/*
输出
初始容量:10 初始限制:10 当前位置:0
当前容量:10 当前限制:10 当前位置:3
reset后的当前位置:1
[10, 20, 3, 0, 0, 0, 0, 0, 0, 0]
 */

7. 其它方法

  • 获取 position 与 limit 之间的元素数:public int remaining()
  • 获取当前缓冲区是否只读:public boolean isReadOnly()
  • 获取当前缓冲区是否为直接缓冲区:public boolean isDirect()
  • 还原缓冲区的状态:public Buffer clear()
    • 将 position 设置为 0;
    • 将限制 limit 设置为容量 capacity;
    • 丢弃标记 mark;
  • 缩小 limit 的范围:public Buffer flip()
    • 将 limit 设置为当前 position 位置;
    • 将当前 position 位置设置为 0;
    • 丢弃标记;
  • 重绕此缓冲区:public Buffer rewind()
    • 将 position 位置设置为 0;
    • 限制 limit 不变;
    • 丢弃标记;
发布了310 篇原创文章 · 获赞 315 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Regino/article/details/105120004
今日推荐