NIO三大核心之缓冲区(Buffer)

缓冲区(Buffer)

基本介绍

缓冲区(Buffer):缓冲区本质上是一个可读可写的内存块。

可以理解成一个数组,该对象提供了一组方法,可以轻松的操作内存块。

缓冲区内置了一些机制,能够跟踪和记录缓冲区的状态变化情况。

Channel提供从网络读取数据的通道,但是读取或者写入数据都必须经过Buffer。

image-20220418165707232

Buffer常用API介绍

  1. Buffer类及其子类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    classDiagram
    direction BT
    class Buffer
    class ByteBuffer
    class CharBuffer
    class DoubleBuffer
    class FloatBuffer
    class IntBuffer
    class LongBuffer
    class ShortBuffer

    ByteBuffer --> Buffer
    CharBuffer --> Buffer
    DoubleBuffer --> Buffer
    FloatBuffer --> Buffer
    IntBuffer --> Buffer
    LongBuffer --> Buffer
    ShortBuffer --> Buffer

    如果图片无法查看,请看这里

    image-20220418174426005

    在NIO中,Buffer是一个顶层父类,他是一个抽象类。常用的缓冲区分别对应byte,char,double,float,int,long,short供7种。

  2. 缓冲区对象创建

    方法名 说明
    static ByteBuffer allocate(长度) 创建byte类型的指定长度的缓冲区
    static ByteBuffer wrap(byte[] array) 创建一个有内容的byte类型的缓冲区

    示例代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    /**
    * 创建缓冲区
    *
    * @name: CreateBufferDemo
    * @author: terwer
    * @date: 2022-04-18 17:38
    **/
    public class CreateBufferDemo {
    public static void main(String[] args) {
    // 1.创建一个指定长度的缓冲区,ByteBuffer为例
    ByteBuffer byteBuffer = ByteBuffer.allocate(4);
    for (int i = 0; i < 4; i++) {
    System.out.println(byteBuffer.get());
    }

    // 在此调用会报错
    // System.out.println(byteBuffer.get());
    System.out.println("==================");
    System.out.println();

    // 2.创建一个有内容的缓冲区
    ByteBuffer wrap = ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8));
    for (int i = 0; i < 4; i++) {
    System.out.println(wrap.get());
    }
    }
    }

    运行结果:

    image-20220418175149177

  3. 缓冲区对象添加数据

    方法名 说明
    Int position()/position(int newPosition) 获取当前要操作的索引/修改当前要操作的索引
    int lkimit()/limit(int newLimit) 最多能操作到哪个索引/修改最多能操作的索引位置
    int capacity() 返回缓冲区的总长度
    int remaining()/boolean hasRemaining() 还有多少能操作的索引个数/是否还能操作
    put (byte b)/put(byte[] src) 添加一个字节/添加字节数组

    image-20220418192657630

    示例代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    /**
    * 添加缓冲区
    *
    * @name: PutBufferDemo
    * @author: terwer
    * @date: 2022-04-18 19:27
    **/
    public class PutBufferDemo {
    public static void main(String[] args) {
    // 1.创建一个指定长度的缓冲区
    ByteBuffer byteBuffer = ByteBuffer.allocate(10);
    System.out.println(byteBuffer.position());// 获取当前索引所在的位置
    System.out.println(byteBuffer.limit());// 最多能操作到哪个索引
    System.out.println(byteBuffer.capacity());// 返回缓冲区总长度
    System.out.println(byteBuffer.remaining());// 还有多少个能操作

    // byteBuffer.position(2);
    // byteBuffer.limit(4);
    // System.out.println();
    // System.out.println("============");
    // System.out.println(byteBuffer.position());// 获取当前索引所在的位置
    // System.out.println(byteBuffer.limit());// 最多能操作到哪个索引
    // System.out.println(byteBuffer.capacity());// 返回缓冲区总长度
    // System.out.println(byteBuffer.remaining());// 还有多少个能操作

    // 添加一个字节
    byteBuffer.put((byte) 97);
    System.out.println();
    System.out.println("============");
    System.out.println(byteBuffer.position());// 获取当前索引所在的位置
    System.out.println(byteBuffer.limit());// 最多能操作到哪个索引
    System.out.println(byteBuffer.capacity());// 返回缓冲区总长度
    System.out.println(byteBuffer.remaining());// 还有多少个能操作

    // 添加一个字节数组
    byteBuffer.put("test".getBytes(StandardCharsets.UTF_8));
    System.out.println();
    System.out.println("============");
    System.out.println(byteBuffer.position());// 获取当前索引所在的位置
    System.out.println(byteBuffer.limit());// 最多能操作到哪个索引
    System.out.println(byteBuffer.capacity());// 返回缓冲区总长度
    System.out.println(byteBuffer.remaining());// 还有多少个能操作

    // 超过缓冲区长度会报错
    // byteBuffer.put("1234567".getBytes(StandardCharsets.UTF_8));
    // System.out.println();
    // System.out.println("============");
    // System.out.println(byteBuffer.position());// 获取当前索引所在的位置
    // System.out.println(byteBuffer.limit());// 最多能操作到哪个索引
    // System.out.println(byteBuffer.capacity());// 返回缓冲区总长度
    // System.out.println(byteBuffer.remaining());// 还有多少个能操作

    // 如果缓冲区满了,可以调整position的位置,会覆盖之前对应索引的值
    byteBuffer.position(0);
    byteBuffer.put("1234567".getBytes(StandardCharsets.UTF_8));
    System.out.println();
    System.out.println("============");
    System.out.println(byteBuffer.position());// 获取当前索引所在的位置
    System.out.println(byteBuffer.limit());// 最多能操作到哪个索引
    System.out.println(byteBuffer.capacity());// 返回缓冲区总长度
    System.out.println(byteBuffer.remaining());// 还有多少个能操作
    }
    }
  4. 缓冲区对象读取数据

    方法名 介绍
    flip() 切换读模式,limit设置position位置,position设置0
    get() 读一个字节
    get(byte[] dst) 读多个字节
    get(int index) 读指定索引的字节
    rewind() 将position设置为0,可重复读
    clear() 切换写模式,position设置为0,limit设置为capacity
    array() 将缓冲区转换成字节数组返回

    flip方法:

    image-20220418195006103

    clear方法:

    image-20220418195106115

    示例代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    /**
    * 从缓冲区读取数据
    *
    * @name: GetBufferDemo
    * @author: terwer
    * @date: 2022-04-18 19:51
    **/
    public class GetBufferDemo {
    public static void main(String[] args) {
    // 1.创建一个指定长度的缓冲区
    ByteBuffer byteBuffer = ByteBuffer.allocate(10);
    byteBuffer.put("0123".getBytes(StandardCharsets.UTF_8));
    System.out.println("position:" + byteBuffer.position());
    System.out.println("limit:" + byteBuffer.limit());
    System.out.println("capacity:" + byteBuffer.capacity());
    System.out.println("remaining:" + byteBuffer.remaining());

    // 切换读模式
    System.out.println();
    System.out.println("=================");
    System.out.println("准备读数据:");
    byteBuffer.flip();
    System.out.println("position:" + byteBuffer.position());
    System.out.println("limit:" + byteBuffer.limit());
    System.out.println("capacity:" + byteBuffer.capacity());
    System.out.println("remaining:" + byteBuffer.remaining());
    for (int i = 0; i < byteBuffer.limit(); i++) {
    System.out.println(byteBuffer.get());
    }
    // 读取完毕后,继续读取会报错,超过limit
    // System.out.println(byteBuffer.get());
    // 读取指定字节
    // System.out.println("读取指定索引:");
    // System.out.println(byteBuffer.get(2));

    System.out.println("读取多个字节:");
    // 重复读取
    byteBuffer.rewind();
    byte[] dst = new byte[4];
    byteBuffer.get(dst);
    System.out.println(new String(dst));

    // 将缓冲区转化为字节数组返回
    System.out.println();
    System.out.println("===========");
    System.out.println("将缓冲区转化为字节数组:");
    byte[] array = byteBuffer.array();
    System.out.println(new String(array));

    // 切换写模式,会覆盖之前所有的值
    System.out.println();
    System.out.println("================");
    System.out.println("切换写模式,覆盖之前的值:");
    byteBuffer.clear();
    byteBuffer.put("test".getBytes(StandardCharsets.UTF_8));
    System.out.println(new String(byteBuffer.array()));
    }
    }

    注意:

    1. capacity:容量(长度) limit:界限(最多能读/写到哪里) position:位置(读/写哪个索引)
    2. 获取缓冲区的数据之前,要先调用flip()方法,重复读需要调用rewind()方法
    3. 再次写数据之前,需要先调用clear()方法,此时数据还未消失。再次写入数据完成,数据覆盖了才会消失。
作者

Terwer

发布于

2022-05-04

更新于

2022-05-04

许可协议

评论