【NIO】学习系列(一)Buffer

1.NIO概念

我们知道常见的IO是阻塞的且效率低下的,为了解决这个问题引入了NIO,NIO在IO的基础上使用了cha[] 、byte[]进行了封装,采用ByteBuffer类来操作数据,而且针对File和Socket的channel,使用同步非阻塞实现高性能处理。网上一大堆,这里不做过多阐述

2.Buffer类

buffer类是一个抽象类已知子类(除了boolean类型,其他基本类型都有对应的buffer)
StringBuffer在NIO没有涉及,字符缓冲可用CharBuffer来做。
ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer

在NIO中并不是直接操作数据的而是操作的缓冲区。进行CRUD都是操作缓冲区,数据的操作是否正确与缓冲区的操作是否得当有很大关系。
在使用传统的IO时,我们通常使用byte[]、或char[]来提升性能,但是数组的API很少,对于大多数功能使用数据无法达到,这里引入的Buffer类。

3.API部分

在Nio技术的缓冲区中,存在4个核心技术点,分别是:

  • capacity 容量
  • limit 限制
  • position 位置
  • mark 标记

这四个值的大小关系如下

0 ≤ mark ≤ position ≤ limit ≤ capacity

capacity介绍

他表示缓冲区包含元素的数量,

capacity不能为负数,且不能更改。

int capacity()返回缓冲区容量。
通过byteBuffer来进行介绍其代码如下所示:

public static void main(String[] args) {
        byte[] bytes = new byte[]{1, 2, 3, 4,5};
        ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
        System.out.println(byteBuffer.capacity());
    }

源码简单分析,如下所示:
在第一步我们在调用的时候其实调用的是ByteBuffer类的wrap方法,而其实在ByteBuffer类的方法中,拿到HeapByteBuffer子类对象-》这个子类对象负责后续工作。其后续工作就是第三步有调用了super方法,将数组的值传递了过来,此时的capacity的值就是数组的长度
在这里插入图片描述
limit介绍

注意:创建buffer时默认limt和capacity是相等的,也就是说没有限制

注意:当limt < mark时则丢弃mark的值

int limit();//返回当前限制下标
Buffer limit(int size);//设置限制的下标

限制存放数据的下标,例如 limit(2)那么从下标为2开始就会不允许存放数据了。
在这里插入图片描述

position介绍

position表示指向缓冲区下标的指针,当put时position会自增,当get时缓冲区也会自增,如果mark大于position那么就丢弃当前mark的值。

int position();//返回指针在缓冲区的位置
Buffer position(int position);//设置指针在缓冲区的位置

可以设置和获取position其代码如下所示

@Test
public void test2(){
    char[] chars = new char[]{'1','2','3','4'};
    CharBuffer buffer = CharBuffer.wrap(chars);
    System.out.println("position => "+buffer.position());
    buffer.position(2);
    buffer.put("d");
    System.out.println("position => "+buffer.position());
    for (int i = 0; i< chars.length;i++){
        System.out.println(chars[i]);
    }
}

注意:当设置了position之后又设置了limit,且position > limit 此时会发生什么?

1.当先设置position = 3后设置limit = 2时

此时会将limit的值赋值给position,因为在设置limit的时候会有判断,是不是position > limit

在这里插入图片描述

2.当先设置limit = 2,position=3时

直接报错,因为在设置position的时候会抛出异常
在这里插入图片描述

int remaining()方法

此方法会返回空闲(没被占用)空间大小,其实就是limit - position

@Test
public void test3(){
    float[] floats = new float[]{1.1f,1.2f,1.3f,1.4f,1.5f};
    FloatBuffer floatBuffer = FloatBuffer.wrap(floats);
    floatBuffer.put(1.11f);
    System.out.println(floatBuffer.remaining());
    floatBuffer.put(1.22f);
    System.out.println(floatBuffer.remaining());
}

mark介绍

方法如下

Buffer mark();

mark的作用是做一个标记,相当于一个传送标记,下次我调用的reset时会传送到这,将position设置为这个位置。要注意:mark不能设置副值,且当mark的值大于position时就失效了会被重置为-1。

测试代码如下

@Test
public void test4(){
    char[] chars = new char[]{'1','2','3','4','5','6'};
    CharBuffer charBuffer = CharBuffer.wrap(chars);
    charBuffer.put('d');
    charBuffer.put('o');
    System.out.println("position -> "+charBuffer.position());
    charBuffer.mark();
    charBuffer.get();
    charBuffer.position(1);
    charBuffer.reset();
    charBuffer.put("n");
    charBuffer.put('g');
    System.out.println("position -> "+charBuffer.position());
    charBuffer.reset();
    System.out.println("position -> "+charBuffer.position());
}

isReadOnly()判断只读

boolean isReadOnly();//查看此缓冲区是否是只读缓冲区

isDirect()直接缓冲区

boolean isDirect();//判断缓冲区是否是直接缓冲区

什么是非直接缓冲区?

如下,在ByteBuffer操作硬盘的数据时,是与中间缓冲区进行读写操作,这样会造成软件运行效率低下,内存占用增加。,使用非直接缓冲区可以解决这个弊端,ByteBuffer直接与硬盘进行数据交换,增加程序运行效率。
在这里插入图片描述

直接缓冲区如下所示
在这里插入图片描述

创建非直接缓冲区?

使用 allocate(int capacity)方法来创建

@Test
public void test5(){
    ByteBuffer buffer = ByteBuffer.allocate(10);
    System.out.println(buffer.isDirect());
}

clear() 还原缓冲区的状态

注意:clear无法清除数据,只是还原limit,position,mark而已,不过下次put数据会覆盖以前数据。

将缓冲区的状态还原,limit设置为capacity的大小,position设置为0,mark标记丢弃,所有一切都还原

final Buffer clear();

flip() 对缓冲区进行反转

当向缓冲区读取数据之后再想读取数据就是使用flip的最佳方式

final buffer flip();//对缓冲区进行反转。

1.设置limit为当前位置

2.将position初始化

3.丢弃mark
在这里插入图片描述

测试代码如下所示

@Test
public void test6(){
    int[] ints = new int[]{0,1,2,3,4,5,6,7};
    IntBuffer buffer = IntBuffer.wrap(ints);
    buffer.put(11);
    buffer.put(22);
    buffer.put(33);
    System.out.println("转换前 position -> "+buffer.position()+" limit -> "+ buffer.limit());
    buffer.flip();
    System.out.println("转换后 position -> "+buffer.position()+" limit -> "+ buffer.limit());
    while(buffer.hasRemaining()){
        System.out.println(buffer.get());
    }
}

hasArray()判断是否有底层实现的数组

final boolean hasArray();//
//源码如下

在这里插入图片描述
重绕缓冲区

将位置position设置为0并丢弃标记

rewind方法常在重新读取数据时使用

注意:他和clear()、flip()有区别

clear()意味着还原一切状态

flip()相当于substring截取,将list赋值为position将position置位0,丢弃mark

而rewind()意思是重新读取,重新写入时使用,他不会改变limit的值

final buffer rewind();//

测试代码如下,当调用rewind()此时position会被置为0

@Test
public void test8(){
    IntBuffer buffer = IntBuffer.wrap(new int[]{1, 2, 3, 4, 5, 6, 7});
    buffer.put(10);
    buffer.put(20);
    buffer.put(30);
    buffer.put(40);
    System.out.println(buffer.position());
    buffer.rewind();
    System.out.println(buffer.position());
}

获得偏移量

返回buffer中实现的数组的第一个数据的偏移量

子类可以不需要添加offset的值,默认为0

final int arryOffset();

如下代码所示,以下的offset都为0,不为0的情况后续在学习

@Test
public void test8(){
    IntBuffer buffer = IntBuffer.wrap(new int[]{1, 2, 3, 4, 5, 6, 7});
    buffer.put(10);
    buffer.put(20);
    buffer.put(30);
    buffer.put(40);
    System.out.println(buffer.arrayOffset());
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值