NIO之缓冲区

基本概念

一、缓冲区(Buffer):在 Java NIO 中负责数据的存取,缓冲区底层就是数组。用于存储不同数据类型的数据

1)根据数据类型的不同,提供对应类型的缓冲区(boolean类型除外)

  • ByteBuffer (最常用,也只有它支持直接缓冲区)
  • CharBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer

上述缓冲区的管理方法几乎一致,通过allocate() 获取缓冲区

二、缓冲区存储数据的两个核心方法

  • 1、put() :存入数据到缓冲区中
  • 2、get() :获取缓冲区中的数据

三、缓冲区的核心属性(mark <= position <= limit <= capacity)

  • position :位置,表示缓冲区中正在操作数据的位置(默认值 0)
  • limit :界限,表示缓冲区中可以操作数据的大小(limit后面的数据不能读写)
  • capacity :容量,表示缓冲区中可以最大存储数据的容量,一旦声明就不可修改
  • mark :标记上次position的位置,需要使用 mark() 方法标记(不标记默认值 -1)
使用 reset() 恢复 position 到标记位置,之前必须使用 mark() 方法标记过
 如果 mark < 0 会抛出异常,说明你就没有 mark() 过,当然不能reset()

四、直接缓冲区与非直接缓冲区

  • 非直接缓冲区:通过 allocate() 方法分配缓冲区,将缓冲区建立在JVM的内存中
  • 直接缓冲区:通过 allocateDirect() 方法分配直接缓冲区,将缓冲区建立在操作系统的物理内存中。可以提高效率

使用示例

下面就来调用一下各个方法,并且查看Buffer在不同状态时各个属性的值吧

简单的不指定长度的读取、写入、重置、清空

@Test
public void test1(){
    String str = "abcde";
    //1.分配一个指定大小的缓冲区
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    System.out.println("=============初始================");
    System.out.println("position:" + buffer.position());
    System.out.println("limit:"+ buffer.limit());
    System.out.println("capacity:" + buffer.capacity());

    //2.向缓冲区中存入数据
    buffer.put(str.getBytes());
    System.out.println("=============put()================");
    System.out.println("position:" + buffer.position());
    System.out.println("limit:"+ buffer.limit());
    System.out.println("capacity:" + buffer.capacity());

    //3.设置缓冲区状态为读取
    buffer.flip();
    System.out.println("=============flip()================");
    System.out.println("position:" + buffer.position());
    System.out.println("limit:"+ buffer.limit());
    System.out.println("capacity:" + buffer.capacity());

    //4.从缓冲区中读取数据
    byte[] bytes = new byte[buffer.limit()];
    buffer.get(bytes);//获取全部数据
    System.out.println("=============get()================");
    System.out.println("position:" + buffer.position());
    System.out.println("limit:"+ buffer.limit());
    System.out.println("capacity:" + buffer.capacity());
    System.out.println(new String(bytes));

    //5.可以重新读取数据
    buffer.rewind();//调用该方法会将position重新指向缓冲区的头部,并清空mark
    buffer.get(bytes);//获取全部数据
    System.out.println("=============rewind()================");
    System.out.println("position:" + buffer.position());
    System.out.println("limit:"+ buffer.limit());
    System.out.println("capacity:" + buffer.capacity());
    System.out.println(new String(bytes));

    //6.清空缓冲区,只是重置各个指针的位置,数据没有真正清除
    buffer.clear();
    System.out.println("=============clear()================");
    System.out.println("position:" + buffer.position());
    System.out.println("limit:"+ buffer.limit());
    System.out.println("capacity:" + buffer.capacity());
    System.out.println("clear后读取第一个字符还是有数据的:" + (char)buffer.get());
}

输出

=============初始================
position:0
limit:1024
capacity:1024
=============put()================
position:5
limit:1024
capacity:1024
=============flip()================
position:0
limit:5
capacity:1024
=============get()================
position:5
limit:5
capacity:1024
abcde
=============rewind()================
position:5
limit:5
capacity:1024
abcde
=============clear()================
position:0
limit:1024
capacity:1024
clear后读取第一个字符还是有数据的:a

指定长度的读取、mark、reset、hasRemaining、remaining

Buffer可以制定长度的读取和写入,两种模式一样,这里就写一遍get,同样的方式也可以用在put上

@Test
public void test2(){
     String str = "abcde";

     ByteBuffer buf = ByteBuffer.allocate(1024);

     buf.put(str.getBytes());
     System.out.println("=============put()================");
     System.out.println("position:" + buf.position());
     System.out.println("limit:"+ buf.limit());
     System.out.println("capacity:" + buf.capacity());

     buf.flip();// 切换到读模式,否则position往后走啥也读不到
     // 1.先读取前两位数据,并mark
     byte[] bytes = new byte[buf.limit()];
     // 一定要注意这个方法的三个参数所表示的含义
     // 这里的第二个参数 0 也就是 offset 指定的是从数组 bytes 的什么位置开始写
     // 而第三个参数 2 也就是 length 表示缓冲区 buf 会从position往后读length长度的数据
     buf.get(bytes, 0 , 2);
     buf.mark(); // 标记一下
     System.out.println("=============get(byte[], int, int)================");
     System.out.println("position:" + buf.position());
     System.out.println("limit:"+ buf.limit());
     System.out.println("capacity:" + buf.capacity());
     System.out.println(new String(bytes));

     // 2.从缓冲区继续读取两位数据
     buf.get(bytes,2, 2);
     System.out.println("=============get(byte[], int, int)================");
     System.out.println("position:" + buf.position());
     System.out.println("limit:"+ buf.limit());
     System.out.println("capacity:" + buf.capacity());
     System.out.println(new String(bytes));

     // 3.查看缓冲区是否还有可操作的位置
     if(buf.hasRemaining()){
         // 输出缓冲区中可以操作的数量
         System.out.println("=============remaining()================");
         System.out.println("remaining:" + buf.remaining());
     }

     // 4.恢复读取的指针到mark标记的位置,使用reset(),并且读取剩余数据
     buf.reset();
     // 这里长度要指定好,不要超出缓冲区可以读取的长度,就是当前的 (limit - position)
     // 否则后面读取的时候会报错的
     byte[] bytes2 = new byte[buf.limit() - buf.position()];
     buf.get(bytes2);//将剩余数据全部读取出来
     System.out.println("=============reset()================");
     System.out.println("position:" + buf.position());
     System.out.println("limit:"+ buf.limit());
     System.out.println("capacity:" + buf.capacity());
     System.out.println(new String(bytes2));
 }

输出

=============put()================
position:5
limit:1024
capacity:1024
=============get(byte[], int, int)================
position:2
limit:5
capacity:1024
ab   
=============get(byte[], int, int)================
position:4
limit:5
capacity:1024
abcd 
=============reset()================
position:5
limit:5
capacity:1024
cde

创建直接缓冲区

  • 直接缓冲区要消耗更多的资源来创建,除非有更大的好处,否则创建 非直接缓冲区
  • 只有 ByteBuffer 支持 直接缓冲区
public void test3(){

    //创建直接缓冲区,只有 ByteBuffer 支持
    ByteBuffer buf = ByteBuffer.allocateDirect(1024);

    //该方法能判断缓冲区类型,true 直接缓冲区, false 非直接缓冲区
    System.out.println(buf.isDirect());
}

输出

true

方法总结

  • get(byte[]) 缓冲区中读取数据写入指定数组
  • put(bute[]) 指定数组中读取数据并写入缓冲区
  • flip() 翻转读写模式,并清空mark
  • rewind() position置为0,并清空mark
  • clear() 清空缓冲区,实际上只是重各个属性的值,并没有清空缓冲区内的数组,也会将读模式切回写模式
  • compact() 如果有数据未读完,但是想切换回写模式先写,可以使用此方法,此方法会将未读数据copy到缓冲区的头部,并将position指到未读数据的尾部,limit重新设置为capacity
  • get(byte[] bytes, int offset, int length) 从缓冲区中读取length长度的数据写入bytes,并从bytes的offset位置开始写
  • put(byte[] bytes, int offset, int length) 从数组的offset位置读取length长度的数据写入缓冲区
  • hasRemaining() 数组中是否还有可以操作的位置,读写都可以调用这个方法,写表示是否还有空间可以写,读表示是否还有数据未读
  • remaining() 获取剩余可以操作的空间大小
  • mark() 记录当前正在操作的位置
  • reset() 恢复到上次mark的位置,注意,必须在调用了mark之后才能调用此方法,而且在上次mark和调用reset之间必须没有调用过清空mark的方法,也就是mark不能为 -1
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值