Java NIO Buffer过程详解

 更新时间:2019年06月13日 10:07:22   作者:xiaoaiwhc1, kevinlinkai, 琪花亿草, 溪边九节  
这篇文章主要介绍了Java NIO Buffer过程详解,缓冲区在java nio中负责数据的存储。缓冲区就是数组。用于存储不同数据类型的数据。,需要的朋友可以参考下

前言

在与NIO通道交互时使用Java NIO Buffer。 如您所知,数据从通道读入缓冲区,并从缓冲区写入通道。

缓冲区本质上是一个可以写入数据的内存块,然后可以再次读取。 此内存块包含在NIO Buffer对象中,该对象提供了一组方法,可以更轻松地使用内存块。

基本缓冲区用法

使用缓冲区读取和写入数据通常遵循这4个小步骤:

1.写入数据到缓冲区

2.调用 buffer.flip()

3.从缓冲区读取数据

4.调用 buffer.clear() 或者 buffer.compact()

当你将数据写入Buffer时,Buffer会跟踪你已经写入了多少数据。一旦你需要读出数据,你需要调用 flip() 方法将Buffer从写模式转换到读模式。在读模式,Buffer允许你将之前写入的数据全部读出。

一旦你已经读出了所有数据,你需要清除Buffer,为下次写入数据做准备。可以通过以下两种方法来完成:clear() 和 compact()。clear() 方法清除整个Buffer,而 compact() 方法仅仅清除你已经读出的Buffer,未读数据会被移动到Buffer的开始位置,再次写入的数据会追加到未读数据的后面。

这是一个简单的 Buffer 使用的例子,使用的 write, flip, read 和 clear 操作:

RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
FileChannel inChannel = aFile.getChannel();
//create buffer with capacity of 48 bytes
ByteBuffer buf = ByteBuffer.allocate(48);int bytesRead = inChannel.read(buf); //read into buffer.
while (bytesRead != -1) { buf.flip(); //make buffer ready for read
while(buf.hasRemaining()){
System.out.print((char) buf.get()); // read 1 byte at a time
} buf.clear(); //make buffer ready for writing
bytesRead = inChannel.read(buf);
}
aFile.close();

Buffer的三个属性:容量,位置和限定符

Buffer本质上是一块你可以写入数据的内存区域,当然你也可以在写入之后读出数据。该内存区域被封装成一个 NIO Buffer 对象,它提供一系列的方法,以方便对该内存区域的操作。

为了学习Buffer 是如何工作的,Buffer 的三个属性你必须要熟悉,它们是:

  • capacity (容量)
  • position (游标位置)
  • limit (末尾限定符)

其中,position 和 limit 的意义依赖于当前 Buffer 是处于读模式还是写模式。capacity 的含义无论读写模式都是相同的。

下面是对以上三个属性在读模式和写模式的一个示例,后面会有详细的解释:

Buffer capacity, position and limit in write and read mode.

Capacity (容量)

作为一个内存块,Buffer 有一个固定的大小,我们叫做 “capacity(容量)"。你最多只能向 Buffer 写入 capacity 大小的字节,长整数,字符等。一旦 Buffer 满了,你必须在继续写入数据之前清空它(读出数据,或清除数据)。

Position (游标位置)

当你开始向 Buffer 写入数据时,你必须知道数据将要写入的位置。position 的初始值为 0。当一个字节或长整数等类似数据类型被写入 Buffer 后,position 就会指向下一个将要写入数据的位置(根据数据类型大小计算)。position 的最大值是 capacity - 1。

当你需要从 Buffer 读出数据时,你也需要知道将要从什么位置开始读数据。在你调用 flip 方法将 Buffer 从写模式转换为读模式时,position 被重新设置为 0。然后你从 position 指向的位置开始读取数据,接下来 position 指向下一个你要读取的位置。

限制(Limit)

在写模式下对一个Buffer的限制即你能将多少数据写入Buffer中。在写模式下,限制等同于Buffer的容量(capacity)。

当切换Buffer为读模式时,限制表示你最多能读取到多少数据。因此,当切换Buffer为读模式时,限制会被设置为写模式下的position值。换句话说,你能读到之前写入的所有数据(限制被设置为已写的字节数,在写模式下就是position)。

Buffer类型

Java NIO提出了如下几种Buffer类型:

  • ByteBuffer
  • MappedByteBuffer
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer

正如你所看到的,这些Buffer类型代表了不同的数据类型。换句话说,他们让你可以在使用的时候用char, short, int, long, float 或者double类型来代替直接使用buffer中的字节。

其中MappedByteBuffer有点特殊,将在它自己的部分来阐述。

分配一个Buffer

若要获取一个Buffer对象,必须先分配它。每个Buffer类都有allocate()函数用来分配。下面的例子展示了分配一个

ByteBuffer,其容量为48字节。

ByteBuffer buf = ByteBuffer.allocate(48);

下面的例子是分配一个具有1024个字符的空间的CharBuffer:

CharBuffer buf = CharBuffer.allocate(1024);

将数据写入Buffer

有两种方法可以将数据写入Buffer:

1.从Channel将数据写入Buffer

2.调用buffer的put()函数,自己将数据写入Buffer。

下面的例子是展示Channel如何将数据写入到Buffer中:

int bytesRead = inChannel.read(buf); //read into buffer.

下面的例子是通过put()函数将数据写入Buffer:

buf.put(127);

put()函数还有很多其他版本,可以让你使用不用的方法将数据写入到Buffer。例如,在特定的位置写,或者将字节数组写入到buffer。查看JavaDoc来了解buffer实现的更多细节。

flip()

用 flip() 方法将 Buffer f从写入模式切换到读取模式。调用 flip() 将 position 设置回0,并将 limit 置为刚才的位置。

换句话说,position 现在标记了读取位置,limit 标记了写入缓冲区的字节、字符数等——可以读取的字节数、字符数等的限制。

从缓冲区读取数据

有两种方法可以从 Buffer 中读取数据。

1.从缓冲区读取数据到通道。

2.使用缓冲区自带方法中的 get() 方法从缓冲区读取数据。

下面是如何将数据从缓冲区读取到通道的例子:

//read from buffer into channel.
int bytesWritten = inChannel.write(buf);

下面是使用 get() 方法从 Buffer 中读取数据的例子:

byte aByte = buf.get();

get() 方法还有许多其他版本,允许您以多种不同的方式从 Buffer 中读取数据。 例如,在特定位置读取,或者从缓冲区读取字节数组。有关具体缓冲区实现的详细信息,请参阅JavaDoc。

rewind()

Buffer.rewind() 将 position 设置回0,因此你可以重读缓冲区中的所有数据。这个 limit 保持不变,因此仍然标记有多少元素(字节、字符等)可以从Buffer读取。

clear() and compact()

从 Buffer 中读取数据之后,必须让 Buffer 为再次写入做好准备。您可以通过调用 clear() 或调用 compact()来做到这一点。

如果您调用 clear() ,position 将被设置为0,并 limit 置为 capacity。换句话说,Buffer 被清除。Buffer 中的数据没有清除。只有标记告诉您可以将数据写入 Buffer 的位置。

当您调用 clear() 时,如果缓冲区中有任何未读数据,则数据将被“遗忘”,这意味着您不再有任何标记来说明哪些数据已被读取,哪些数据未被读取。

如果 Buffer 中仍然有未读数据,并且您希望稍后读取它,但是您需要先写一些东西,那么调用 compact() 而不是 clear().

compact() 将所有未读的数据复制到 Buffer 的开头。然后将 position 设置为最后一个未读元素之后的位置。与 clear() 一样,limit 属性仍然设置为 capacity。现在 Buffer 已经准备好写入,但是不会覆盖未读数据。

mark() and reset()

你可以调用 Buffer.mark() 方法在 Buffer 中标记给定位置。之后,你可以调用 Buffer.reset() 方法重置回标记的这个位置。下面是个例子:

buffer.mark();
//call buffer.get() a couple of times, e.g. during parsing.
buffer.reset(); //set position back to mark.

equals() and compareTo()

用equals() 和 compareTo()方法可以比较两个缓冲区。

equals()

两个缓冲相同,如果:

1.他们是同一个类型(byte,char,int等)。

2.在缓冲区,它们遗留有相同量的字节、字符等。

3.所有遗留的字节、字符都相同。

正如你看到的,equals只比较Buffer的一部分,而不是每个元素。事实上,它只比较Buffer中遗留的元素。

compareTo()

用 compareTo() 方法比较两个缓冲区的遗留元素(字节、字符等),用在例如排序例程。在下列情况中,一个缓冲区被视为“小于”另一个缓冲区,如果:

与另一个缓冲区对应元素相等的第一个元素,小于另一个缓冲区的元素。

所有的元素都相等,但是第一个缓冲区在第二个缓冲区之前耗尽了元素(它有更少的元素)。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • springboot返回值转成JSONString的处理方式

    springboot返回值转成JSONString的处理方式

    这篇文章主要介绍了springboot返回值转成JSONString的处理方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • Java图形用户界面设计(Swing)的介绍

    Java图形用户界面设计(Swing)的介绍

    看到多数人提到 Java 就以为是网络开发,其实不是这样的,Java 也可以开发应用程序,而且可以开发出漂亮的图形用户界面的应用程序,因此,我写下这篇文章,希望能带你进入 Java 图形用户界面设计之门。
    2016-07-07
  • java.io.File的renameTo方法移动文件失败的解决方案

    java.io.File的renameTo方法移动文件失败的解决方案

    这篇文章主要介绍了java.io.File的renameTo方法移动文件失败的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • 详谈java线程与线程、进程与进程间通信

    详谈java线程与线程、进程与进程间通信

    下面小编就为大家带来一篇详谈java线程与线程、进程与进程间通信。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-04-04
  • java中DES加密解密

    java中DES加密解密

    本文给大家分享的是一段java中实现des加密解密的代码,非常的实用,基本每个项目都可以用到,推荐给大家。
    2015-03-03
  • SpringBoot项目中接口防刷的完整代码

    SpringBoot项目中接口防刷的完整代码

    本文通过实例代码给大家介绍了SpringBoot项目中接口防刷的方法,代码简单易懂,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2021-07-07
  • java设计模式学习之工厂方法模式

    java设计模式学习之工厂方法模式

    这篇文章主要介绍了java设计模式学习之工厂方法模式,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-10-10
  • SpringBoot 整合 Shiro 密码登录的实现代码

    SpringBoot 整合 Shiro 密码登录的实现代码

    这篇文章主要介绍了SpringBoot 整合 Shiro 密码登录的实现,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-02-02
  • springboot集成测试容器重启问题的处理

    springboot集成测试容器重启问题的处理

    这篇文章主要介绍了springboot集成测试容器重启问题的处理,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • 关于Eureka的概念作用以及用法详解

    关于Eureka的概念作用以及用法详解

    这篇文章主要介绍了关于Eureka的概念作用以及用法详解,服务治理就是提供了微服务架构中各微服务实例的快速上线或下线且保持各服务能正常通信的能力的方案总称,需要的朋友可以参考下
    2023-05-05

最新评论