Java NIO Buffer过程详解

前言

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

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

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

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

(0)

相关推荐

  • 浅谈Java中BIO、NIO和AIO的区别和应用场景

    最近一直在准备面试,为了使自己的Java水平更上一个档次,拜读了李林峰老师的<Netty权威指南>,了解了Java关于IO的发展和最新的技术,真是受益匪浅,现在把我总结的关于BIO.NIO和AIO的区别和应用场景概述一遍. 在此之前,先弄清几个概念: 1.同步:使用同步IO时,Java自己处理IO读写. 2.异步:使用异步IO时,Java将IO读写委托给OS处理,需要将数据缓冲区地址和大小传给OS,完成后OS通知Java处理(回调). 3.阻塞:使用阻塞IO时,Java调用会一直阻塞到读写完成

  • java8中NIO缓冲区(Buffer)的数据存储详解

    java8新特性NIO缓冲区(Buffer)的数据存储. ByteBuffer,CharBuffer,ShortBuffer,IntBuffer,LongBuffer,FloatBuffer, DoubleBuffer. 1.缓冲区在java nio中负责数据的存储.缓冲区就是数组.用于存储不同数据类型的数据.根据数据类型不同(boolean除外),提供了相应类型的缓冲区. ByteBuffer,CharBuffer,ShortBuffer,IntBuffer,LongBuffer,FloatB

  • 详解java nio中的select和channel

    什么是NIO? 线程在处理数据时,如果线程还处于将数据从channel读到buffer的这段时间内,线程可以去做别的事情,等数据都读到buffer了,线程再回来处理读到的数据 channel是什么? 类比流的概念.与流的区别在于 1.channel是可读可写的,但是一个流要么写要么读 2.chanel可以异步的读和写 3.数据总是从channel中读到buffer,或者从buffer中写到channel 流的读取或写一般是一次性的操作,数据在读取过程中不会有缓存,这也就意味着没有办法自己随便移动

  • Java NIO.2 使用Path接口来监听文件、文件夹变化

    Java7对NIO进行了大的改进,新增了许多功能: •对文件系统的访问提供了全面的支持 •提供了基于异步Channel的IO 这些新增的IO功能简称为 NIO.2,依然在java.nio包下. 早期的Java只提供了File类来操作文件.文件夹本身,功能有限,性能也不高. NIO.2为解决这种缺陷,提供了Path接口,并提供了Paths.Files2个工具类,这2个工具类包含的方法都是静态方法,Files类提供了大量的静态方法来操作文件.文件夹. Path接口.Paths工具类使用示例: //获

  • 泛谈Java NIO

    前言 非阻塞IO,也被称之为新IO,它重新定义了一些概念. 1.缓冲buffer 2.通道 channel 3.通道选择器 BIO 阻塞IO,几乎所有的java程序员都会的字节流,字符流,输入流,输出流等分类就是针对BIO而言的.我们在使用BIO的时候都是建立基本的节点流然后用过滤流进行包装. 不同于BIO,NIO所有的IO操作都是通过通道读写buffer完成的.数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中. 通道 NIO的通道类似流,但是有所不同. 1.既可以从通道中读取数据,又可以

  • Java NIO框架Netty简单使用的示例

    之前写了一篇文章:Java 网络IO编程总结(BIO.NIO.AIO均含完整实例代码),介绍了如何使用Java原生IO支持进行网络编程,本文介绍一种更为简单的方式,即Java NIO框架. Netty是业界最流行的NIO框架之一,具有良好的健壮性.功能.性能.可定制性和可扩展性.同时,它提供的十分简单的API,大大简化了我们的网络编程. 同Java IO介绍的文章一样,本文所展示的例子,实现了一个相同的功能. 1.服务端 Server: package com.anxpp.io.calculat

  • Java NIO Buffer过程详解

    前言 在与NIO通道交互时使用Java NIO Buffer. 如您所知,数据从通道读入缓冲区,并从缓冲区写入通道. 缓冲区本质上是一个可以写入数据的内存块,然后可以再次读取. 此内存块包含在NIO Buffer对象中,该对象提供了一组方法,可以更轻松地使用内存块. 基本缓冲区用法 使用缓冲区读取和写入数据通常遵循这4个小步骤: 1.写入数据到缓冲区 2.调用 buffer.flip() 3.从缓冲区读取数据 4.调用 buffer.clear() 或者 buffer.compact() 当你将

  • Java NIO服务器端开发详解

    一.NIO类库简介 1.缓冲区Buffer Buffer是一个对象,包含一些要写入和读出的数据. 在NIO中,所有的数据都是用缓冲区处理的,读取数据时,它是从通道(Channel)直接读到缓冲区中,在写入数据时,也是从缓冲区写入到通道. 缓冲区实质上是一个数组,通常是一个字节数组(ByteBuffer),也可以是其它类型的数组,此外缓冲区还提供了对数据的结构化访问以及维护读写位置等信息. Buffer类的继承关系如下图所示: 2.通道Channel Channel是一个通道,网络数据通过Chan

  • Java NIO通信基础示例详解

    目录 Java NIO 通信基础介绍 NIO 和 OIO 的对比 使用 FileChannel 完成文件复制的实践案例 使用 DatagramChannel 数据包通道发送数据的实践案例 使用 NIO 实现 Discard 服务器的实践案例 Java NIO 通信基础介绍 高性能的 Java 通信,绝对离不开 Java NIO 技术,现在主流的技术框架或中间件服务器,都使 用了 Java NIO 技术,譬如:Tomcat.Jetty.Netty. Java NIO 由以下三个核心组件组成: Ch

  • Java NIO Selector用法详解【含多人聊天室实例】

    本文实例讲述了Java NIO Selector用法.分享给大家供大家参考,具体如下: 一.Java NIO 的核心组件 Java NIO的核心组件包括:Channel(通道),Buffer(缓冲区),Selector(选择器),其中Channel和Buffer比较好理解 简单来说 NIO是面向通道和缓冲区的,意思就是:数据总是从通道中读到buffer缓冲区内,或者从buffer写入到通道中. 关于Channel 和 Buffer的详细讲解请看:Java NIO 教程 二.Java NIO Se

  • Java类加载的过程详解

    目录 Java:类加载过程 1.加载--3件事 2.连接 2.1.验证 2.2.准备 2.3.解析 3.初始化 总结 Java:类加载过程 1.加载--3件事 1.通过全类名获取定义此类的二进制字节流(eg:从jar.war中获取): 2.将字节流所代表的静态存储结构转换为方法区的运行时数据结构: 3.在内存中生成一个代表该类的Class对象,作为方法区这些数据的访问入口. 2.连接 加载阶段和连接阶段的部分内容是交叉进行的,加载尚未结束,连接阶段可能就开始运行了. 2.1.验证 2.2.准备

  • docker搭建minio及java sdk使用过程详解

    目录 1minio简洁 2 docker搭建minio 2.1 单节点 2.2 多节点部署 3 java sdk使用minio 1minio简洁 MinIO 是一款高性能.分布式的对象存储系统. 它是一款软件产品, 可以100%的运行在标准硬件.即X86等低成本机器也能够很好的运行MinIO. MinIO与传统的存储和其他的对象存储不同的是:它一开始就针对性能要求更高的私有云标准进行软件架构设计.因为MinIO一开始就只为对象存储而设计.所以他采用了更易用的方式进行设计,它能实现对象存储所需要的

  • Java NIO Buffer实现原理详解

    目录 1.Buffer的继承体系 2.Buffer的操作API使用案例 3.Buffer的基本原理 4.allocate方法初始化一个指定容量大小的缓冲区 5.slice方法缓冲区分片 6.只读缓冲区 7.直接缓冲区 8.内存映射 1.Buffer的继承体系 如上图所示,对于Java中的所有基本类型,都会有一个具体的Buffer类型与之对应,一般我们最经常使用的是ByteBuffer. 2.Buffer的操作API使用案例 举一个IntBuffer的使用案例: /** * @author csp

  • java通过Jsoup爬取网页过程详解

    这篇文章主要介绍了java通过Jsoup爬取网页过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一,导入依赖 <!--java爬虫--> <dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.10.3</version> </depe

  • Java并发编程预防死锁过程详解

    这篇文章主要介绍了Java并发编程预防死锁过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 在java并发编程领域已经有技术大咖总结出了发生死锁的条件,只有四个条件都发生时才会出现死锁: 1.互斥,共享资源X和Y只能被一个线程占用 2.占有且等待,线程T1已经取得共享资源X,在等待共享资源Y的时候,不释放共享资源X 3.不可抢占,其他线程不能强行抢占线程T1占有的资源 4.循环等待,线程T1等待线程T2占有的资源,线程T2等待线程T1占有

  • java property配置文件管理工具框架过程详解

    这篇文章主要介绍了java property配置文件管理工具框架过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 property property 是 java 实现的 property 框架. 特点 优雅地进行属性文件的读取和更新 写入属性文件后属性不乱序 灵活定义编码信息 使用 OO 的方式操作 property 文件 支持多级对象引用 快速开始 环境依赖 Maven 3.x Jdk 1.7+ Maven 引入依赖 <depende

随机推荐