NIO-缓冲区(1)

缓冲区

缓冲区的两个重要组件:状态变量和访问方法 (accessor)。

状态变量 每一个读/写操作都会改变缓冲区的状态。通过记录和跟踪这些变化,缓冲区就可能够内部地管理自己的资源。

访问方法 在从通道读取数据时,数据被放入到缓冲区。在有些情况下,可以将这个缓冲区直接写入另一个通道,但是在一般情况下,您还需要查看数据,使用 访问方法 get() 来完成的。同样,如果要将原始数据放入缓冲区中,就要使用访问方法 put()。

NIO读写一文中,介绍写文件时对flip()allocate()做了相应的阐述,已经对缓冲区内部机制有了一些介绍。


状态变量

可以用三个值指定缓冲区在任意时刻的状态:

  • position
  • limit
  • capacity

这三个变量一起可以跟踪缓冲区的状态和它所包含的数据。

  • mark <= position <= limit <= capacity
  • mark : 标示了缓冲区中执行reset操作时,position应该置于的位置
  • position : 标示缓冲区中下一个能够进行读写的位置
  • limit : 标示缓冲区中第一个不能进行读写的位置
  • capacity: 用来指定缓冲区的最大容量,它是不变的

position

缓冲区实际上就是丰富化的数组。

在从通道读取时,您将所读取的数据放到底层的数组中。 position 变量跟踪已经写了多少数据。更准确地说,它指定了下一个字节将放到数组的哪一个元素中。因此,如果您从通道中读三个字节到缓冲区中,那么缓冲区的 position 将会设置为3,指向数组中第四个元素。

在写入通道时,您是从缓冲区中获取数据。 position 值跟踪从缓冲区中获取了多少数据。更准确地说,它指定下一个字节来自数组的哪一个元素。因此如果从缓冲区写了5个字节到通道中,那么缓冲区的 position 将被设置为5,指向数组的第六个元素。

limit

limit 变量表明还有多少数据需要取出(在从缓冲区写入通道时),或者还有多少空间可以放入数据(在从通道读入缓冲区时)。

position <= limit。

capacity

缓冲区的 capacity 表明可以储存在缓冲区中的最大数据容量。实际上,它指定了底层数组的大小 ― 至少是指定了准许我们使用的底层数组的容量。

limit <= capacity。

观察变量变化

  • 我们创建一个8个字节的缓冲区

allocate(8) 从源码中可以看出:
在调用allocate()方法分配缓冲区时,实际上将limit 和 capacity 设置成了同样的大小。更多细节可以参考 Buffer & ByteBuffer源码

public static ByteBuffer allocate(int capacity) {
        if (capacity < 0)
            throw new IllegalArgumentException();
        return new HeapByteBuffer(capacity, capacity);
    }
创建缓冲区

limit <= capacity 所以这两个都指向数组的尾部一个不存在的位置

position 设置为0。如果我们写一些数据到缓冲区中,那么下一个写入的数据就进入 slot 0 。如果我们从缓冲区读一些数据,从缓冲区读取的下一个字节也来自 slot 0 。

position
  • 第一次写入缓冲区

首先从输入通道中读一些数据到缓冲区中。第一次读取得到3个字节。它们被放到数组中从 position 开始的位置,这时 position 初始位置为 0。写入之后,position 就增加到 3,如下所示:

第一次写入缓冲区
  • 第二次写入缓冲区

在第二次写入时,我们从输入通道读取另外2个字节到缓冲区中。这两个字节储存在由 position 所指定的位置上, position 因而增加 2:

第二次写入缓冲区
  • buffer.flip()

flip() 从源代码可以看出,该方法将limit指向了缓冲区当前位置 position,并将position设置为0,将mark丢弃

public final Buffer flip() {
       limit = position;
       position = 0;
       mark = -1;
       return this;
   }
buffer#flip()

现在可以将数据从缓冲区写入通道了。 position 被设置为 0,这意味着我们得到的下一个字节是第一个字节。 limit 已被设置为原来的 position,这意味着它包括以前读到的所有字节,并且一个字节也不多。

  • 第一次读取缓冲区

我们从缓冲区中取4个字节并将它们写入输出通道。这使得 position 增加到 4,而 limit 不变,如下所示:

第一次读取缓冲区

204.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

  • 第二次读取缓冲区

只剩下一个字节可读了。 limit在我们调用 flip() 时被设置为 5,并且 position 不能超过 limit。所以最后一次写入操作从缓冲区取出一个字节并将它写入输出通道。这使得 position 增加到 5,并保持 limit 不变,如下所示:

第二次读取缓冲区
  • buffer.clear()

clear() 从源码中我们可以看出 该方法将position设置为0,limit归位与capacity保持一致

public final Buffer clear() {
        position = 0;
        limit = capacity;
        mark = -1;
        return this;
    }
buffer#clear()

访问方法

到目前为止,我们只是使用缓冲区将数据从一个通道转移到另一个通道。然而,程序经常需要直接处理数据。例如:可能需要将用户数据保存到磁盘,在这种情况下,必须将这些数据直接放入缓冲区,然后用通道将缓冲区写入磁盘。
或者,可能想要从磁盘读取用户数据,在这种情况下,要将数据从通道读到缓冲区中,然后检查缓冲区中的数据。

put()方法

ByteBuffer 类中有五个 put() 方法:

  1. ByteBuffer put( byte b ); // 写入单个字节
  2. ByteBuffer put( byte src[] ); // 写入来自一个数组的一组字节
  3. ByteBuffer put( byte src[], int offset, int length ); // 写入来自一个数组的一组字节
  4. ByteBuffer put( ByteBuffer src ); // 将数据从一个给定的源 ByteBuffer 写入这个 ByteBuffer
  5. ByteBuffer put( int index, byte b ); // 将字节写入缓冲区中特定的 位置

返回 ByteBuffer 的方法只是返回调用它们的缓冲区的 this 值。

与 get() 方法一样,我们将把 put() 方法划分为 相对 或者 绝对 的。前四个方法是相对的,而第五个方法是绝对的。
上面显示的方法对应于 ByteBuffer 类。其他类有等价的 put() 方法,这些方法除了不是处理字节之外,其它方面是完全一样的。它们处理的是与该缓冲区类相适应的类型。

类型化的 get() 和 put() 方法

除了前些小节中描述的 get() 和 put() 方法, ByteBuffer 还有用于读写不同类型的值的其他方法,如下所示:

  • getByte()
  • getChar()
  • getShort()
  • getInt()
  • getLong()
  • getFloat()
  • getDouble()
  • putByte()
  • putChar()
  • putShort()
  • putInt()
  • putLong()
  • putFloat()
  • putDouble()

事实上,这其中的每个方法都有两种类型 ― 一种是相对的,另一种是绝对的。它们对于读取格式化的二进制数据(如图像文件的头部)很有用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容