从jdk1.4开始,java中引入了nio包,提供了非阻塞式的网络编程模型,提供网络性能。nio中核心组件有三个:channel、buffer、selector。这里主要探讨buffer的概念和使用。buffer本质上是数据容器,可以存储java中的各种原始数据类型,并提供了读、写等各种操作。

1. 什么是buffer

buffer是一个数据容器,确切的说是存储原始数据类型的数据容器(除了boolean类型)。

  • ByteBuffer:存储byte类型的数据
  • CharBuffer:存储char类型的数据
  • ShortBuffer:存储short类型的数据
  • IntBuffer:存储int类型的数据
  • LongBuffer:存储long类型的数据
  • FloatBuffer:存储float类型的数据
  • DoubleBuffer:存储double类型的数据

2. buffer中的核心属性

  • capacity:代表buffer的容量,即可以容纳的元素数量。capacity>0,且不能改变。
  • limit:向容器内写入数据或者从容器内读取数据时,不能等于或超过limit对应的索引。0<limit<=capacity
  • position:向容器内写入数据或者从容器内读取数据时,下一个元素的索引。0<=position<limit
  • mark:标记位,当调用buffer中的reset方法时,position会被重置到mark对应的位置。注意:必须先调用mark方法设置mark值,才能调用reset方法,否则会抛出InvalidMarkException。

三者之间的关系: mark<= position <= limit <= capacity

3. buffer中的get和put操作

buffer中的get和put操作分为两类:相对get/put操作(relative get/put operation)和绝对get/put操作(absolute get/put operation)。

  • 相对操作(relative operation):从当前的position开始,读取/写入一个或多个元素,然后增加position的值(根据元素数量改变position的值)。如果相对get操作超出limit的限制,会抛出BufferUnderflowException;如果相对put操作超出limit限制,会抛出BufferOverflowException。
  • 绝对操作(absolute operation):显式的指定index来读取/写入一个元素。absolute operation可能会导致IndexOutofBoundsException。 注意:absolute operation操作不会改变position的值

get和put操作是在Buffer的子类中定义的。

4. buffer中其它常用操作

  • capacity():获取buffer的容量

  • position():获取当前的position值

  • limit():获取当前的limit值

  • mark():设置mark变量的值,mark=position

  • reset():重置position,position=mark

  • clear():清空buffer,position=0, limit=capacity, mark=-1。该方法通常在使用通道读取数据或者put操作填充buffer之前调用。 注意:该方法并不会真的删除buffer中的数据。

  • flip():翻转buffer,limit=position, position=0, mark=-1。执行该方法为读取数据做准备。

  • rewind():执行该方法后,position=0, mark=-1,通常用于重新读取buffer中的数据。

  • remaining():获取buffer中的元素数量(limit-position)。

  • array():获取buffer中的数组。注意:如果改变buffer中的元素,会影响返回的数组中元素,反之亦然。

5. 解读buffer的常见操作引起的容器变化

这里我以CharBuffer为例,解释buffer中的常见操作,以及容器是如何变化的。

下图展示了put(char c)操作(relative operation)引起的一些列变化,

(1)首先新建一个容量为5的buffer,次数position=0,limit=5(图中未标出来);

(2)第1次调用 put(char c),写入元素a,此时position变为1,limit不变;

(3)第2次调用put(char c),写入元素b,此时position变为2,limit不变;

(4)第2次调用put(char c),写入元素c,此时position变为3,limit不变;

(5)第2次调用put(char c),写入元素d,此时position变为4,limit不变;

(6)第2次调用put(char c),写入元素e,此时position变为5,limit不变(limit=position)

接下来演示flip()操作和get操作(relative operation)给buffer容器带来的变化

(1)执行flip()操作后,position=0,limit=5;

(2)执行get()操作后,返回元素a,position=1,limit=5;

(3)执行get()操作后,返回元素b,position=2,limit=5;

(4)执行get()操作后,返回元素c,position=3,limit=5;

(5)执行get()操作后,返回元素d,position=4,limit=5;

(6)执行get()操作后,返回元素e,position=5,limit=5;

注意:执行get()操作,容器内的元素并不会被删除。

绝对get和put操作我就不在这里演示了,只要容器里存在元素,你随时可以通过绝对get和put操作,读取、写入元素。

注意:绝对get和put操作不会抛出BufferUnderflowException和BufferOverflowException。

如果这时我们对buffer容器执行rewind()或者clear()操作,容器变化如下:

执行rewind操作后,position=0,limit保持不变(=5),容器内元素又变得可以读取了。因此,当我们想要重复读取容器内元素的时候,就可以借助rewind()来帮助我们达成目的。

注意:执行clear()操作后,position=0,limit=capacity=5,虽然这个时候我们仍然可以对buffer容器执行get操作,但是我们不应该这么做!!clear操作的目的是清空缓存区,为之后的channel-read或put操作服务的,使得它们可以再次填充buffer容器。

接下来我们再来看一下mark和reset操作,假设容器内部情况如下图:

容器内又五个元素,position=0,limit=5,接下来我们依此对容器执行下述几步操作:

get() ==> get() ==> mark() ==> get() ==> get() ==> reset() ==> get() ==> get()

容器变化情况如下图所示:

可以看到,通过mark和reset,我们可以重复读取某一区间内的元素。

6. 测试代码

CharBuffer buffer = CharBuffer.allocate(5);
char[] chars = buffer.array();
System.out.println(String.format("未存储任何元素之前,limit=%d, position=%d, chars=%s", buffer.limit(), buffer.position(), Arrays.toString(chars)));
// 写入第1个元素
buffer.put('a');
System.out.println(String.format("写入1个元素之后,limit=%d, position=%d, chars=%s", buffer.limit(), buffer.position(), Arrays.toString(chars)));
// 写入第2个元素
buffer.put('b');
System.out.println(String.format("写入2个元素之后,limit=%d, position=%d, chars=%s", buffer.limit(), buffer.position(), Arrays.toString(chars)));
// 写入第3个元素
buffer.put('c');
System.out.println(String.format("写入3个元素之后,limit=%d, position=%d, chars=%s", buffer.limit(), buffer.position(), Arrays.toString(chars)));
// 写入第4个元素
buffer.put('d');
System.out.println(String.format("写入4个元素之后,limit=%d, position=%d, chars=%s", buffer.limit(), buffer.position(), Arrays.toString(chars)));
// 写入第5个元素
buffer.put('e');
System.out.println(String.format("写入5个元素之后,limit=%d, position=%d, chars=%s", buffer.limit(), buffer.position(), Arrays.toString(chars)));
/*
// 如果继续往容器中写入元素,buffer会抛出BufferOverflowException
buffer.put("f"); // 执行这行代码会抛出BufferOverflowException异常
*/ // 执行flip操作,为读取buffer中的数据做准备
buffer.flip();
System.out.println("\n执行flip操作后---------------");
System.out.println(String.format("未读取任何元素之前,limit=%d, position=%d", buffer.limit(), buffer.position()));
System.out.println(String.format("读取1个元素之后,limit=%d, position=%d,当前读取的元素:%s", buffer.limit(), buffer.position(), buffer.get()));
System.out.println(String.format("读取2个元素之后,limit=%d, position=%d,当前读取的元素:%s", buffer.limit(), buffer.position(), buffer.get()));
System.out.println(String.format("读取3个元素之后,limit=%d, position=%d,当前读取的元素:%s", buffer.limit(), buffer.position(), buffer.get()));
System.out.println(String.format("读取4个元素之后,limit=%d, position=%d,当前读取的元素:%s", buffer.limit(), buffer.position(), buffer.get()));
System.out.println(String.format("读取5个元素之后,limit=%d, position=%d,当前读取的元素:%s", buffer.limit(), buffer.position(), buffer.get()));
// buffer.get(); // 执行这行代码会抛出BufferUnderflowException // 执行rewind操作,重新读取buffer中的数据
buffer.rewind();
System.out.println("\n执行rewind操作后---------------");
System.out.println(String.format("未读取任何元素之前,limit=%d, position=%d", buffer.limit(), buffer.position()));
System.out.println(String.format("读取1个元素之后,limit=%d, position=%d,当前读取的元素:%s", buffer.limit(), buffer.position(), buffer.get()));
System.out.println(String.format("读取2个元素之后,limit=%d, position=%d,当前读取的元素:%s", buffer.limit(), buffer.position(), buffer.get()));
System.out.println(String.format("读取3个元素之后,limit=%d, position=%d,当前读取的元素:%s", buffer.limit(), buffer.position(), buffer.get()));
System.out.println(String.format("读取4个元素之后,limit=%d, position=%d,当前读取的元素:%s", buffer.limit(), buffer.position(), buffer.get()));
System.out.println(String.format("读取5个元素之后,limit=%d, position=%d,当前读取的元素:%s", buffer.limit(), buffer.position(), buffer.get())); System.out.println("\n测试absolute put/get operation");
System.out.println(buffer.get(0));
buffer.put(0, 's');
System.out.println(buffer.get(0));
System.out.println(String.format("limit=%d, position=%d", buffer.limit(), buffer.position())); // 执行clear操作后,不应该再调用get()方法获取容器内元素!
/*
buffer.clear();
System.out.println(String.format("未读取任何元素之前,limit=%d, position=%d", buffer.limit(), buffer.position()));
System.out.println(String.format("读取1个元素之后,limit=%d, position=%d,当前读取的元素:%s", buffer.limit(), buffer.position(), buffer.get()));
System.out.println(String.format("读取2个元素之后,limit=%d, position=%d,当前读取的元素:%s", buffer.limit(), buffer.position(), buffer.get()));
System.out.println(String.format("读取3个元素之后,limit=%d, position=%d,当前读取的元素:%s", buffer.limit(), buffer.position(), buffer.get()));
System.out.println(String.format("读取4个元素之后,limit=%d, position=%d,当前读取的元素:%s", buffer.limit(), buffer.position(), buffer.get()));
System.out.println(String.format("读取5个元素之后,limit=%d, position=%d,当前读取的元素:%s", buffer.limit(), buffer.position(), buffer.get()));
*/ System.out.println("\n测试mark、reset");
buffer.rewind(); // 调用rewind,使得我们可以重新读取buffer中的元素
Assert.assertEquals('s', buffer.get());
Assert.assertEquals('b', buffer.get());
buffer.mark(); // mark=2,position=2
Assert.assertEquals('c', buffer.get());
Assert.assertEquals('d', buffer.get());
buffer.reset(); // position重新指向2,即position=2
Assert.assertEquals('c', buffer.get());
Assert.assertEquals('d', buffer.get());

7. buffer不是线程安全的

解读Java NIO Buffer的更多相关文章

  1. 全面解读Java NIO工作原理(3)

    全面解读Java NIO工作原理(3) 2011-12-14 10:31 Rollen Holt Rollen Holt的博客 我要评论(0) 字号:T | T JDK 1.4 中引入的新输入输出 ( ...

  2. 全面解读Java NIO工作原理(2)

    全面解读Java NIO工作原理(2) 2011-12-14 10:31 Rollen Holt Rollen Holt的博客 我要评论(0) 字号:T | T JDK 1.4 中引入的新输入输出 ( ...

  3. 全面解读Java NIO工作原理(1)

    全面解读Java NIO工作原理(1) 2011-12-14 10:31 Rollen Holt Rollen Holt的博客 我要评论(0) 字号:T | T JDK 1.4 中引入的新输入输出 ( ...

  4. 全面解读Java NIO工作原理(4)

    全面解读Java NIO工作原理(4) 2011-12-14 10:31 Rollen Holt Rollen Holt的博客 我要评论(0) 字号:T | T JDK 1.4 中引入的新输入输出 ( ...

  5. Java NIO Buffer(netty源码死磕1.2)

    [基础篇]netty源码死磕1.2:  NIO Buffer 1. Java NIO Buffer Buffer是一个抽象类,位于java.nio包中,主要用作缓冲区.Buffer缓冲区本质上是一块可 ...

  6. (二:NIO系列) Java NIO Buffer

    出处:Java NIO Buffer Buffer是一个抽象类,位于java.nio包中,主要用作缓冲区.Buffer缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存.这块内存被包装成NIO ...

  7. java.nio.Buffer 中的 flip()方法

    在Java NIO编程中,对缓冲区操作常常需要使用  java.nio.Buffer中的 flip()方法. Buffer 中的 flip() 方法涉及到 Buffer 中的capacity.posi ...

  8. java NIO Buffer 详解(1)

    1.java.io  最为核心的概念是流(stream),面向流的编程,要么输入流要么输出流,二者不可兼具: 2.java.nio 中拥有3个核心概念: Selector Channel, Buffe ...

  9. [翻译] java NIO Buffer

    原文地址:http://tutorials.jenkov.com/java-nio/buffers.html JAVA NIO 是在和channel交互的时候使用的.正如你所知道的,数据是从chann ...

随机推荐

  1. 安装Ubuntu 出现ubi partman crashed,ubi-partman failed with exit code 10

    出现这个问题好像是因为硬盘中有遗留的raid信息导致. 在安装选项中添加 nodmraid参数,再启动进行安装就好了 如果不会添加参数可以参考这篇文章:安装ubuntu时黑屏三种解决办法 就和添加 n ...

  2. Object.prototype.__proto__, [[prototype]] 和 prototype

    Object.prototype.__proto__ , [[prototype]] 和 prototype Object.prototype.__proto__ 是什么? __proto__ 是一个 ...

  3. Chevereto免费图床搭建教程 | vps搭建免费图床教程

    Chevereto免费图床搭建教程 | vps搭建免费图床教程 本文首发于:https://chens.life/How-to-build-a-free-image-bed.html 前言 自从有了个 ...

  4. Nordic 52840-Timer定时器学习问题(一)

    今天在ble_app_blinky例程中移植定时器驱动,在编译过程中报出了两个错误,在此记录一下. 1. 在nRF_Dreivers中添加nrfx_timer.c文件 选中“nRF_Dreivers  ...

  5. golang gorm 基本使用

    原文链接:golang orm 框架之 gorm gorm 用法介绍 库安装 go get -u github.com/jinzhu/gorm 数据库连接 import ( "github. ...

  6. BIGI行情http请求实时行情数据方式

    BIGI行情http请求实时行情数据方式 新浪财经文华财经并非实时行情数据源,所以获取的行情数据源也并非实时的.以下介绍的方法和新浪财经获取行情数据源的方法是一致的.需要实时行情数据源可以向BIGI行 ...

  7. python numpy常用的数学和统计函数

    numpy模块的核心就是基于数组的运算,相比于列表和其他数据结构,数组的运算效率是最高的.在统计分析和挖掘过程中,经常会使用到numpy模块的函数,以下是常用的数学函数和统计函数: 常数p就是圆周率  ...

  8. 怎么下载chrome的扩展程序

    很多时候我们是没办法访问谷歌扩展应用程序 chrome应用商店的,这时候我们最好能把对应扩展应用程序下载保存,以便提供给其他人员使用. 搜索得到知乎有很全的方法: 如何导出并打包第三方chrome扩展 ...

  9. HTML5移动开发之路(1)——jqMobi中Side Menu实现(类似人人网)

    记得以前在做Native App的时候类似于人人网侧边滑动的效果非常的热,很多app仿照该效果进行开发,在jqMobi中也有类似的效果被称为Side Menu.下面我们来一步一步实现该效果. 首先新建 ...

  10. C# Mongo DB 修改多层嵌套集合中的字段

    C# Mongo DB 修改嵌套集合中的字段 虽然c#的mongo 驱动很强大,而且还支持linq,但是一些复杂的操作语句还是比较困难 这里我用Bson实现功能 这是模型(我这里有多层嵌套) publ ...