Buffer是个啥?

Buffer 即缓冲区,用来暂存输入输出数据的区域。Buffer对象是一份固定数量的数据的容器,实质上是一个数组。但是一个缓冲区不仅仅是一个数组,缓冲区提供了对数据的结构化访问,还可以跟踪系统的读/写进程。
 
在 Java传统 IO 中,数据直接写入或者将数据直接读到 Stream 对象中。
而在 NIO 中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的。在写入数据时,它是写入到缓冲区中的。任何时候访问 NIO 中的数据,都是将它放到缓冲区中。
NIO中的Buffer主要用于与NIO通道(channel)进行交互,数据是从通道读入缓冲区,从缓冲区写入通道中的。
 

继承关系图

Buffer作为一个容器用来存储数据的,根据存储数据类型的不同,分为以下七种:
ByteBuffer,CharBuffer,ShortBuffer,IntBuffer,LongBuffer,FloatBuffer,DoubleBuffer

主要属性

// 标记、位置、限制、容量遵守以下不变式:
// 0<=mark<=position<=limit<=capacity // 标记是一个位置索引,可以在之后设置位置为标记值再次读写。
private int mark = -1; // 位置,表示缓冲区中正在操作数据的位置。
// 可以把position理解成一个指针,指向的位置就是操作的位置。
private int position = 0; // 界限,表示缓冲区中可以操作数据的大小。
private int limit; // 容量,表示缓冲区中最大存储数据的容量。一旦声明,不可改变。
private int capacity; // 指向缓冲区的地址
long address;

构造方法

// 创建一个具有给定标记、位置、限制和容量的新缓冲区
Buffer(int mark, int pos, int lim, int cap) {
// 缓冲区的初始化容量不能小于0
if (cap < 0)
throw new IllegalArgumentException("Negative capacity: " + cap);
// 初始化缓冲区容量
this.capacity = cap;
// 初始化缓冲区限制,设置最新的缓冲区Buffer
limit(lim);
position(pos);
if (mark >= 0) {
if (mark > pos)
throw new IllegalArgumentException("mark > position: ("
+ mark + " > " + pos + ")");
this.mark = mark;
}
}

关键方法解析

position方法:设置当前操作位置

public final Buffer position(int newPosition) {
// 当前操作位置不能超过limit大小,不能小于0
if ((newPosition > limit) || (newPosition < 0))
throw new IllegalArgumentException();
position = newPosition;
// 如果标记位置大于正在操作位置,那么将mark设置为-1。
if (mark > position) mark = -1;
return this;
}

limit方法:设置缓冲区限制

public final Buffer limit(int newLimit) {
// newLimit大小不能超过缓冲区最大容量capacity,不能小于0
if ((newLimit > capacity) || (newLimit < 0))
throw new IllegalArgumentException();
limit = newLimit;
// 正在操作数据的位置不能大于界限
if (position > limit) position = limit;
// 如果标记位置大于界限,那么将mark设置为-1。
if (mark > limit) mark = -1;
return this;
}

reset方法:将缓冲区Buffer的位置position设置为以前临时备忘变量mark存储的位置

public final Buffer reset() {
int m = mark;
if (m < 0)
throw new InvalidMarkException();
position = m;
return this;
}
clear方法:清除缓冲区,clear方法并没有将之前的数据真的清除,当读数据前调用flip方法后,limit会限制不让使用旧数据。
public final Buffer clear() {
position = 0;
// 虽然将limit数值设置为最大容量,只是为了正常使用buffer
limit = capacity;
mark = -1;
return this;
}

flip方法: 反转此缓冲区,在进行写完去读的时候使用,说白了,让你开始读的时候从初始位置0开始。

public final Buffer flip() {
// 将position置为0,其实就是切换读写模式
limit = position;
position = 0;
mark = -1;
return this;
}

rewind方法:重绕此缓冲区,在通道写入或者获取之前调用

public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}

remaining方法:返回当前的缓冲区Buffer中剩余元素的数量

public final int remaining() {
return limit - position;
}

总结

1、Buffer中所有的方法操作,都遵守以下不变式:
 0<=mark<=position<=limit<=capacity
2、flip方法,让数据开始读的时候从初始位置0开始。
3、clear方法其实是个障眼法,并没有清除之前数据内容。

盘一盘 NIO (一)—— Buffer源码解析的更多相关文章

  1. Git8.3k星,十万字Android主流开源框架源码解析,必须盘

    为什么读源码 很多人一定和我一样的感受:源码在工作中有用吗?用处大吗?很长一段时间内我也有这样的疑问,认为哪些有事没事扯源码的人就是在装,只是为了提高他们的逼格而已. 那为什么我还要读源码呢?一刚开始 ...

  2. Python优秀开源项目Rich源码解析

    这篇文章对优秀的开源项目Rich的源码进行解析,OMG,盘他.为什么建议阅读源码,有两个原因,第一,单纯学语言很难在实践中灵活应用,通过阅读源码可以看到每个知识点的运用场景,印象会更深,以后写代码的时 ...

  3. Scala 深入浅出实战经典 第65讲:Scala中隐式转换内幕揭秘、最佳实践及其在Spark中的应用源码解析

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...

  4. Scala 深入浅出实战经典 第61讲:Scala中隐式参数与隐式转换的联合使用实战详解及其在Spark中的应用源码解析

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载: 百度云盘:http://pan.baidu.com/s/1c0noOt ...

  5. Scala 深入浅出实战经典 第60讲:Scala中隐式参数实战详解以及在Spark中的应用源码解析

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...

  6. Scala 深入浅出实战经典 第48讲:Scala类型约束代码实战及其在Spark中的应用源码解析

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-64讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...

  7. Netty 4源码解析:服务端启动

    Netty 4源码解析:服务端启动 1.基础知识 1.1 Netty 4示例 因为Netty 5还处于测试版,所以选择了目前比较稳定的Netty 4作为学习对象.而且5.0的变化也不像4.0这么大,好 ...

  8. Netty5客户端源码解析

    Netty5客户端源码解析 今天来分析下netty5的客户端源码,示例代码如下: import io.netty.bootstrap.Bootstrap; import io.netty.channe ...

  9. Netty5服务端源码解析

    Netty5源码解析 今天让我来总结下netty5的服务端代码. 服务端(ServerBootstrap) 示例代码如下: import io.netty.bootstrap.ServerBootst ...

随机推荐

  1. Lucene03--字段属性

    Lucene03--字段属性 1.Field 1.1  Field相当于Javabean的属性. 1.2  不同的Field的构造方法参数不一样: 大多数Field的构造函数有三个参数: a)第一个参 ...

  2. js继承的关系(一)

    js继承的关系多,而且拥有不同的特点.同时也是必须了解掌握的知识点.首先,要先知道什么是构造函数? 构造函数 构造函数和普通函数的区别:仅在于调用方式不同,任何函数,只要通过 new 操作符来调用,那 ...

  3. MySQL图形工具SQLyog破解版

    最近一直在用MySQL,所以分享一下现在这套开发工具. SQLyog:链接:http://pan.baidu.com/s/1bLq2OA 密码:h5bj 注册信息用户名:yunjian注册码:81f4 ...

  4. linux初学者-正则表达式

      在windows中,是没有办法批量处理文件的,但是在linux系统中,可以通过命令直接处理一系列文件,这些文件的处理就需要用到正则表达式.这同样可以应用与目录. 1.处理多个文件 正则表达式中批量 ...

  5. Ubuntu16.04配置Django服务器环境

    本环境是使用Ubuntu16.04+Django2.1.0+python3.6+uwsgi+nginx进行配置 1. 安装python3.6 Ubuntu16.04中原本安装的是python3.5,但 ...

  6. DFS_全球变暖_蓝桥杯2018省赛N0.9

    你有一张某海域NxN像素的照片,"."表示海洋."#"表示陆地,如下所示: ....... .##.... .##.... ....##. ..####. .. ...

  7. Java中Random随机数

    java中一般有两种随机数,一个是Math中random()方法,一个是Random类. 一.Math.random() 随即生成0<=x<1的小数. 实例:如何写,生成随机生成出0~10 ...

  8. 基于redis的消息订阅与发布

    Redis 的 SUBSCRIBE 命令可以让客户端订阅任意数量的频道, 每当有新信息发送到被订阅的频道时, 信息就会被发送给所有订阅指定频道的客户端. 作为例子, 下图展示了频道 channel1  ...

  9. threeJS创建mesh,创建平面,设置mesh的平移,旋转、缩放、自传、透明度、拉伸

    这个小案例是当初我在学习的时候,小的一个小案例,代码还需要进一步优化:还请谅解~~:主要用到了threeJS创建mesh,创建平面,设置mesh的平移,旋转.缩放.自传.透明度.拉伸等这些小功能: 采 ...

  10. ansible-service

    #service#查询服务状态 ansible server01 -m service -a "name=httpd state=started" #停止服务 ansible se ...