首页 > 编程语言 >Java NIO中的Buffer类

Java NIO中的Buffer类

时间:2022-10-20 01:44:46浏览次数:41  
标签:info capacity intBuffer NIO Buffer limit position Java logger

Buffer类

当应用程序进行数据传输的时候,往往需要使用缓冲区,常用的缓存区就是JDK NIO类库提供的 java.nio.Buffer

NIO的Buffer本质上是一个内存块,既可以写入数据,也可以从中读取数据;

其中,Java NIO中代表缓冲区的Buffer类是一个抽象类,对应于Java的主要数据类型,在NIO中有8种缓存区,分别如下:ByteBuffer,CharBuffer,DoubleBuffer,FloatBuffer,IntBuffer,LongBuffer,ShortBuffer,MappedByteBuffer;前7种 Buffer类型,覆盖了能在 IO中传输的所有的 Java基本数据类型,第8种类型MappedByteBuffer是专门用于内存映射的一种 ByteBuffer类型;

NIO的Buffer的内部是一个内存块(数组),此类用来与普通的内存块(Java数组)不同的是:Buffer对象提供了一组比较有效的方法,用来进行写入和读取的交替访问;

 

注:Buffer类是一个非线程安全类;

 

Buffer类的重要属性

Buffer的子类会拥有一块内存,作为数据的读写缓冲区,但是读写缓冲区并没有定义在Buffer基类,而是定义在具体的子类中;

为了记录读写的状态和位置,Buffer类额外提供了一些重要的属性,如下:

  • capacity

Buffer类的capacity属性,表示缓冲区中的最大数据容量;一旦写入的对象数量超过了capacity容量,缓冲区就满了,不能再写入,而且Buffer类的capacity属性一旦初始化,就不能更改,因为Buffer类的对象在初始化时,它会按照capacity分配内部数组的内存,在数组分配好内存之后,它的大小则不能更改,比如capacity为1024的IntBuffer,代表其一次可以存储1024个int类型的值;

注:capacity容量并不是指Buffer内部的内存块byte[]数组的字节数量,而是指能写入的数据对象最大限制量;如在ByteBuffer中内部的内存块存储在ByteBuffer#hb成员属性上,该数组的长度是可以扩容和压缩的;

 

  • position

Buffer类的position属性,表示当前的位置,即被写入或者读取的元素索引;position属性的值与缓冲区的读写模式有关,在不同的模式下,position属性值的含义是不同的,在缓存区进行读写的模式改变时,position值会进行相应的调整;

写模式下的position值变化规则

在写模式下,position值变化规则如下:

  1. 在刚进入到写入模式时position值为0,表示当前的写入位置为从头开始;
  2. 每当一个数据写到缓冲区后,position会向后移动到下一个可写的位置;
  3. 初始的position值为0,最大可写值为limit - 1,当position值达到limit时,缓冲区就已经无空间可写了;

 

读模式下的position值变化规则

在读模式下,position的值变化规则如下:

  • 当缓冲区刚开始进入到读取模式时,position会被重置为0;
  • 当从缓冲区读取时,从position位置开始读;读取数据后,position向后移动到下一个可读的位置;
  • 在读模式下,limit表示可以读上限;position的最大值,为最大可读上限limit,当position达到limit时,表明缓冲区已经无数据可读;

 

Buffer的读写模式切换

当新建了一个缓冲区实例时,缓冲区处于写入模式,这时是可以写数据的;在数据写入完成后,如果要从缓冲区读取数据,这就要进行模式的切换,可以使用(即调用) flip翻转方法,将缓冲区变成读取模式;

从写入模式到读取模式的flip方法翻转过程中, position和limit属性值会进行调整,规则如下:

  • limit属性被设置成写入模式时的position值,表示可以读取的最大数据位置;
  • position由原来的写入位置,变成新的可读位置,即0的位置,表示可以从头开始读;

 

  • limit

Buffer类的limit属性,表示还有多少数据需要取出(在从缓冲区写入通道时),或者还有多少空间可以放入数据(在从通道读取缓冲区时),limit属性值的含义与缓冲区的读写模式有关;

  • 在写入模式下,limit属性值的含义为可以写入的数据最大上限;在刚进入到写入模式时, limit的值会被设置成缓冲区的capacity容量值,表示可以一直将缓冲区的容量写满;
  • 在读取模式下,limit属性值的含义为最多能从缓冲区读取多少数据;

 

limit值在读写模式下的取值
  1. 当新创建的缓冲区时,Buffer处于写入模式,其position值为0,limit值为最大容量capacity;
  2. 往缓冲区写入数据,每写一个数据,position向后偏移一个位置,即position值加1;
  3. 当调用flip方法时,将缓冲区切换到读模式,将写入模式下的position的值设置成读模式下的limit的值,即写入模式下的position的值为读模式下的limit的值;
java.nio.Buffer#flip

  • mark

标记着当前position可读或可写的一个备份值,可供后续恢复时使用;

在缓冲区操作(读取或写)的过程中,可以将当前的position的值,临时存入mark属性中;在需要恢复的时候,可以再从mark中取出之前的值,恢复到positioin属性中,后续可以重新从position为重开始处理(读取或写);

java.nio.Buffer#mark

 

Buffer类的重要方法

Buffer#allocate 创建缓冲区

如果需要获取一个Buffer实例对象,并不是使用子类的构造器来创建一个实例对象,而是调用子类的allocate方法;

查看代码
@Test
public void testAllocate() {
    IntBuffer intBuffer = null;
    intBuffer = IntBuffer.allocate(20);
    logger.info("------------after allocate------------------");
    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());
}

例子中,IntBuffer是具体的Buffer子类,通过调用IntBuffer.allocate(20),创建了一个Intbuffer实例对象,并且分配了 20 * 4个字节的内存空间;

执行结果如下:

mark,position,capactiy,limit关系图如下:

一个缓冲区在新建后,处于写入的模式,position属性的值为 0,缓冲区的capacity容量值也是初始化时 allocate方法的参数值,而limit最大可写上限值也为的allocate方法的初始化参数值;

 

Buffer#put 写入缓冲区

在调用allocate方法分配内存、返回了实例对象后,缓冲区实例对象处于写模式,可以写入对象,而如果要写入对象到 缓冲区,需要调用put方法;

查看代码
 @Test
public void testPut() {
    IntBuffer intBuffer = IntBuffer.allocate(20);
    logger.info("------------after allocate------------------");
    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());

    for (int i = 0; i < 5; i++) {
        intBuffer.put(i);
    }

    logger.info("------------after putTest------------------");
    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());
}

执行结果如下:

mark,position,capactiy,limit关系图如下:

从结果可以看到,写入了5个元素之后,缓冲区的position属性值变成了5(从下标0开始),所以指向了第6个可以进行写入的元素位置,而 limit最大可写上限、capacity最大容量两个属性的值,都没有发生变化;

 

Buffer#flip 翻转

flip翻转方法是Buffer类提供的一个模式转变的重要方法,它的作用就是将写入模式翻转成读取模式;

查看代码
@Test
public void flipTest() {
    IntBuffer intBuffer = null;
    intBuffer = IntBuffer.allocate(20);
    logger.info("------------after allocate------------------");
    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());


    for (int i = 0; i < 5; i++) {
        intBuffer.put(i);

    }

    logger.info("------------after putTest------------------");
    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());


    intBuffer.flip();
    logger.info("------------after flipTest ------------------");

    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());
}

执行结果如下:

mark,position,capactiy,limit关系图如下:

 

缓冲区从读取模式切换到写入模式

通过调用Buffer#clear清空或Buffer#compact压缩,它们可以将缓冲区转换为写入模式;Buffer模式转换如下:

Buffer#get 从缓冲区读取

调用flip方法将缓冲区切换成读取模式之后,就可以开始从缓冲区中进行数据读取,通过调用get方法每次从position的位置读取一个数据,并且进行相应的缓冲区属性的调整;

查看代码
@Test
public void getTest() {
    IntBuffer intBuffer = null;
    intBuffer = IntBuffer.allocate(20);
    logger.info("------------after allocate------------------");
    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());


    for (int i = 0; i < 5; i++) {
        intBuffer.put(i);

    }

    logger.info("------------after putTest------------------");
    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());


    intBuffer.flip();
    logger.info("------------after flipTest ------------------");

    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());

    for (int i = 0; i < 2; i++) {
        int j = intBuffer.get();
        logger.info("intBuffer[" + i + "]:" + j);
    }

    logger.info("------------after get 2 int ------------------");
    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());

    for (int i = 0; i < 3; i++) {
        int j = intBuffer.get();
        logger.info("intBuffer[" + i + "]:" + j);
    }
    logger.info("------------after get 3 int ------------------");
    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());
}

执行结果如下:

读取操作会改变可读位置position的属性值,而limit可读上限值并不会改变;在position值和limit的值相等时,表示所有数据读取完成,position指向了一个没有数据的元素位置,已经不能再读了,此时再读,会抛出BufferUnderflowException异常;

mark,position,capactiy,limit关系图如下:

处于读取模式下,不能对缓冲区进行数据写入,需要调用Buffer#clear或Buffer#compact方法,即清空或压缩缓冲区,将缓冲区切换成写入模式,让缓冲区重新可写;

 

Buffer#rewind 倒带

已经读完的数据,如果需要再读一遍,可以调用rewind方法;

查看代码
@Test
public void getTest() {
    IntBuffer intBuffer = null;
    intBuffer = IntBuffer.allocate(20);
    logger.info("------------after allocate------------------");
    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());


    for (int i = 0; i < 5; i++) {
        intBuffer.put(i);

    }

    logger.info("------------after putTest------------------");
    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());


    intBuffer.flip();
    logger.info("------------after flipTest ------------------");

    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());

    for (int i = 0; i < 2; i++) {
        int j = intBuffer.get();
        logger.info("intBuffer[" + i + "]:" + j);
    }

    logger.info("------------after get 2 int ------------------");
    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());

    for (int i = 0; i < 3; i++) {
        int j = intBuffer.get();
        logger.info("intBuffer[" + i + "]:" + j);
    }
    logger.info("------------after get 3 int ------------------");
    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());

    intBuffer.rewind();
    logger.info("------------after rewind ------------------");
    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());
}

执行结果如下:

java.nio.Buffer#rewind

rewind方法,主要是调整了缓冲区的 position属性与 mark标记属性,调整规则如下:

  1. position重置为 0,所以可以重读缓冲区中的所有数据;
  2. limit保持不变,数据量还是一样的,仍然表示能从缓冲区中读取的元素数量;
  3. mark标记被清理,表示之前的临时位置不能再用了;

在调用rewind方法后,就可以再一次读取Buffer;

 

Buffer#mark和Buffer#reset

Buffer#mark方法和Buffer#reset方法是成套使用的,Buffer#mark方法将当前position的值保存起来,放在mark属性中,让mark属性记录这个临时位置,之后可以调用Buffer#reset方法将mark的值恢复到position中;

查看代码
@Test
public void resetTest() {
    IntBuffer intBuffer = IntBuffer.allocate(20);
    logger.info("------------after allocate------------------");
    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());


    for (int i = 0; i < 5; i++) {
        intBuffer.put(i);

    }

    logger.info("------------after putTest------------------");
    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());

    intBuffer.flip();
    logger.info("------------after flipTest ------------------");

    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());

    for (int i = 0; i < 5; i++) {
        if (i == 2) {
            intBuffer.mark();
        }
        int j = intBuffer.get();
        logger.info("intBuffer[" + i + "]:" + j);

    }
    logger.info("------------after mark------------------");
    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());

    intBuffer.reset();
    logger.info("------------after reset------------------");
    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());
    for (int i = 2; i < 5; i++) {
        int j = intBuffer.get();
        logger.info("intBuffer[" + i + "]:" + j);
    }
}

执行结果如下:

java.nio.Buffer#mark

上面例子,在读到第3个元素时调用mark方法,把当前位置position的值保存到mark属性中,这时mark属性的值为 2;

Buffer#mark调用后,mark,position,capactiy,limit关系图如下:

java.nio.Buffer#reset

上面例子在调用reset方法后,把mark中的值恢复到position中,因此读取的位置position就是 2,表示可以再次开始从第3个元素开始读取数据;

Buffer#reset调用后,mark,position,capactiy,limit关系图如下:

 

Buffer#clear 清空缓冲区

在读取模式下,调用clear方法将缓冲区切换为写入模式;

查看代码
@Test
public void clearTest() {
    IntBuffer intBuffer = IntBuffer.allocate(20);
    logger.info("------------after allocate------------------");
    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());


    for (int i = 0; i < 5; i++) {
        intBuffer.put(i);

    }

    logger.info("------------after putTest------------------");
    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());

    intBuffer.flip();
    logger.info("------------after flipTest ------------------");

    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());

    for (int i = 0; i < 5; i++) {
        if (i == 2) {
            intBuffer.mark();
        }
        int j = intBuffer.get();
        logger.info("intBuffer[" + i + "]:" + j);

    }
    logger.info("------------after mark------------------");
    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());

    intBuffer.reset();
    logger.info("------------after reset------------------");
    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());
    for (int i = 2; i < 5; i++) {
        int j = intBuffer.get();
        logger.info("intBuffer[" + i + "]:" + j);

    }

    intBuffer.clear();
    logger.info("------------after clear------------------");
    logger.info("position=" + intBuffer.position());
    logger.info("limit=" + intBuffer.limit());
    logger.info("capacity=" + intBuffer.capacity());
}

执行结果如下:

Buffer#clear调用后,mark,position,capactiy,limit关系图如下:

在缓冲区处于读取模式时,调用clear方法,缓冲区会被切换成写入模式,清空了position的值,其值被设置为0,并且limit值为最大容量(capacity);

 

java.nio.Buffer#clear

此方法的作用如下:

  • 将position属性清0;
  • limit设置为capacity最大容量值,可以一直写入,直到缓冲区写满;
  • mark属性赋值为-1;

标签:info,capacity,intBuffer,NIO,Buffer,limit,position,Java,logger
From: https://www.cnblogs.com/coder-zyc/p/16797645.html

相关文章

  • JavaScript异步编程
    单线程:JavaScript这语言被设计的时候本来就是单线程的异步:程序执行后,不会马上开始生效,而是过一会儿才开始行动为什么要用promise(承诺)?为了解决回调地狱通过promi......
  • Java基础数据类型
    目录一、数据类型分类整数类型字符类型布尔类型浮点类型二、数据类型的转换自动类型转换强制类型转换表达式类型的自动提升三、浮点类型计算精度丢失问题一、数据类型分类......
  • JAVA设计模式-代理模式
    JAVA设计模式-代理模式一、介绍代理模式是一种结构型模式,它指的是给某一个对象提供一个代理对象,并且由代理对象控制原有对象的引用,可以增强原有对象的功能以及降低系统......
  • JavaScript学习--String对象,自定义对象,window对象
    String对象定义:var变量名=newString(s);varstr=newString("hello");var变量名=s;           varstr="hello";属性:length字符串长度方法:c......
  • Oracle Buffer Cache 中 Keep Pool 说明
     一.KeepPool说明在我之前的Blog里对DBbuffer进行了一个说明,参考:           ​​OracleBufferCache原理​​           ​​http://www.......
  • JavaWeb项目编译前后的目录结构
    JavaWeb项目编译前后的目录结构编译前页面和视图都放在webapp目录下编译后webappsWEB-INF......
  • 微信授权登录:移动端[unionid](一)
    如果你有多端登录统一用户,或者是同一产品下不同子产品之间统一用户的需求的话,请提前在微信开放平台打通微信网页授权并绑定,不然的话后期打通用户处理起来比较麻烦。因为同一......
  • Java I/O(2):NIO中的Channel
    您好,我是湘王,这是我的51CTO博客,欢迎您来,欢迎您再来~为了解决标准JavaI/O令人难以忍受的效率问题,从JDK1.4开始,NIO出现了(Non-blockingI/O,官方称之为NewI/O)。NIO不但新增加了......
  • 【JavaWeb】会话的学习笔记:Cookie和Session的知识点,这一次我总算学明白了
    @[Toc]1会话1.1什么是会话?用户打开浏览器,访问Web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应。1.2会话跟踪一种维护浏览器状......
  • 小新学Java2
    一、方法1.方法有返回值和无返回值的区别 2.使用方法时的注意事项①方法应该定义在类中,但是不能在方法中再定义方法。不能嵌套。②方法定义的前后顺序无无所谓。③......