美文网首页程序员
【原创】掌握对 ByteBuffer 的操控感

【原创】掌握对 ByteBuffer 的操控感

作者: 星巴刻 | 来源:发表于2017-11-23 13:02 被阅读0次

作者:星巴刻

        作为 Java Nio 的一个基础部分,其提供的 java.nio.ByteBuffer 不易被正确使用简直让人无语,无人愿意为它辩白。ByteBuffer 本质只是 byte 数组的封装,但是与 byte 数组相比起来,要理解好,需要耗费点脑力。本文尝试用一种新的结构来解释 ByteBuffer,用以加速正确、轻松地掌握 ByteBuffer 的使用,希望通过本文的铺垫再去看 ByteBuffer 的注释、源代码以及应用代码,能掌握一种操控感。

一、工作区

        ByteBuffer 虽然是 byte 数组的封装,但是应用程序如何使用数组是受约束的,极少直接通过指定数组下标的方式使用 ByteBuffer。

        在此引入「工作区」的概念,用来助力理解。

        工作区是一个两边伸缩变动长度的区域,它的最左边是始点(用 position 表示),右边是它的终点(用 limit 表示)。现在请用你的右手掌挡住工作区的终点(limit 处),左手掌从工作区的左边 position 处往右压。这个过程中,右边将保持静止(淡定的静止),随着左手掌往右动,有节奏地一动一停地往右,就像脉冲一样的节奏往右。这样,整个工作区将越来越小,position 位置渐渐地向 limit 点靠拢,直至两只手掌合在一起,此时 position 点和 limit 点重合。

        要是觉得这个动作有点幼稚,那就对了,说明完全掌握 ByteBuffer 其实也没有很大难度。

        工作区的大小,可由 limit - position 来表示,这也就是 ByteBuffer.remaining() 的实现

工作区是 ByteBuffer 中最重要的要点(没有之一)

二、完成区 & 禁区

        在工作区的左右两侧另外分别有 1 个区域:工作区的左侧是完成区,右侧是禁区,如下图。

        这样整个 ByteBuffer 3 个区的结构就构建完毕了。这 3 个区先后顺序是固定的,但大小是变化的。3个区的宽度大小,最小可为 0,最大可为 capacity 大。3 个区看起来还是很乱,请不要被这个影响,一定要把注意力优先投资到两个手掌之间的工作区,这样已经足够。现在竖起双手掌,由于手是可以动的,所以左手表示的 position 以及右手表示的 limit 是可以变动的,特别是左手变动是最频繁。随着动作,工作区大小产生了变化,自然而然地也带动了左边完成区以及右边禁区的变化。

工作区两侧的完成区与禁区

三、读/写操作

在工作区上的读写操作

        当对 ByteBuffer 进行操作时,所有操作都是在工作区上完成的!

        进行 get() 时,每一次 get() 的调用,工作区中的 position 位置的字节被读出来,随后工作区的始点向右运动一个位置。随着不断地 get(),工作区的始点一点一点地往右运动,越变越小。// 手势做起来哈,右手掌不动,左手掌往右手掌的方向动,get 一次,动一次。尽量多做几遍

        同理的,进行 put() 时,每一次 put() 调用,字节都写入到工作区的 position 位置中,随后工作区的始点向右运动一个位置。随着不断地 put(),工作区的始点一点一点地往右运动,越变越小。// put 和 get 的手势完全一样

        一旦工作区大小变为 0 了,读写操作就不能再进行了,禁区是不可用于读写操作的。如果强行继续读取或写入,ByteBuffer 将分别抛出 BufferUnderflowException 或 BufferOverflowException 异常。

四、reset() 回到原先设置的 mark 处

reset 回到原先设置的 mark 处

        随着 ByteBuffer 不断地工作,工作区始点逐渐往右运动,工作区越变越小。此时如果要重读刚才读取的内容,或者覆盖原先写入的内容,就可以调用 reset() 方法来满足这个需求,将工作区的始点拉回之前设置的 mark 点。

        reset() 操作必须和 mark() 操作结合使用。调用 mark() 时候,ByteBuffer 会把当时工作区的始点记录下来(用 mark 表示这个位置),

        调用 reset() 方法并不会把 mark 标识清除,后续可以多次使用。如果之前没有 mark() 过或者 mark 标识被 rewind()、flip()、clear() 这些操作清理过,调用 reset() 没有意义,ByteBuffer 会抛出异常。此时如果要回到某个点,建议直接使用 ByteBuffer.position(int) 搞定,所谓调用 position(int) 的本质也就是应用程序自己来维护 mark 记录,这也是一个好办法。

        注意:reset 方法不是把缓冲区的字节设置为 0。

        练习:如何用手势来模拟 reset() 操作呢?其实非常简单,保持右手掌不懂,左手掌向左稍微挪动几步。

五、rewind() 倒带重来

        英文单词 rewind 有重倒的意思。调用 rewind() 就是把工作区的始点拉到 0 处,使得接下来的工作区从 ByteBuffer 的最开始处工作。这个有啥用呢?想来想去可能在「复读」这个场景比较有用:

      当一个 ByteBuffer 要写到多个输出源的时候可以用得上:写入到第一个输出源后,完成区变大,工作区变小,通过调用 rewind() ,把工作区的始点拉到 ByteBuffer 最开始的地方,这样就可以重新从读取刚才已经读取的字节了。

        在 ByteBuffer下 rewind() 就是 position(0)。所以,实际使用起来,直接使用 position(0) 可能更容易理解?另外一个区别点, position(int) 方法在 ByteBuffer 上,没在 Buffer 上。

        练习:如何用手势来模拟 rewind() 操作呢?保持右手掌不懂,左手掌向左伸直移动到最大的可能就是了。// reset() 和 rewind() 在手势上的区分就是看左手伸的多少,到之前标记的是 reset(),伸到尽头的是 rewind()

四、flip() 翻转工作区

        英文单词 flip 的意思有翻、转的意思,比如海狮在沙滩上玩耍翻来翻去,调皮的同学在地上做个腾空翻等等类似的意思。

        把 flip 用在 ByteBuffer 上,主要是用来表达一个动机:对 ByteBuffer 完成写入的工作后,要开始从它里面读取信息。ByteBuffer要求,当对它从写入到读取的变化,需要应用程序来告知 ByteBuffer 提前做一些内部翻转工作,flip() 方法充当这个作用,由应用程序来调用。

flip() 使 Buffer 进行了一次内部翻转

        现在深入到 flip() 内部。当程序不断把数据写到 ByteBuffer,完成区 将越来越大,充满了刚刚写入的数据,此时如果要将写入的数据读取出来,根据 ByteBuffer 的哲学,就需要先把这块完成区区域设置为 工作区 才能在这片区域上工作,按应用程序的预期完成任务。把完成区完全设置为工作区的操作工程中要注意 3 个细节就是:(1)新的工作区的终点就是原来完成区的终点、原来工作区的始点;(2)新的工作区的始点在最左边,因此新的工作区和旧的工作区大小没有任何关系,所以两者大小也不相等。(3) 旧的工作区变成现在新的工作区的右边了,所以它成为禁区的一部分。

        flip() 这个方法是 ByteBuffer 的关键方法,重点记住这个方法吧。

        练习:竖起两手的手掌,两只手中间代表的是 flip() 之前的工作区。然后两只手掌一起往左运动运动。左手掌拉伸到最左边的尽头,右手掌变动原来左手掌的位置。

六、clear() 全部变为工作区

clear() 让 ByteBuffer 做好了最大准备

        clear() 把缓冲区全部变为工作区,工作区最大也不过如此了。clear() 操作是唯一一个把禁区变为工作区一部分的操作。可见 clear() 目的,就是让 ByteBuffer 有最大的工作空间去容纳一会进来的字节。显然,当 ByteBuffer 的信息全部被用来后,准备要从输入源中读出新的信息写入 ByteBuffer 时,要调用 clear()。

        注意:clear 方法不是把缓冲区的字节设置为 0。

        练习:竖起两手的手掌,然后分别向两边拉伸到尽头!

七、总结

          用工作区的概念及其图解、手势的方式来理解 ByteBuffer,是本文的创新点。借助两手手掌模拟工作区,并演示 get、put、reset、rewind、position(i)、flip、clear 操作对手掌位置的影响可以有效地理解和记忆。这种办法对其他人有没有用我不清楚,反正自己是用上了,也轻松了许多。

          如果以上有助于理解,接下来可以直接看下 java.nio.Buffer 的 Java Doc ,看看是否可以清晰一些,这个过程也是一次「思维加固」。

2017-11-23

原文地址:http://www.jianshu.com/p/0343289302a8

相关文章

  • 【原创】掌握对 ByteBuffer 的操控感

    作者:星巴刻 作为 Java Nio 的一个基础部分,其提供的 java.nio.ByteBuffer 不...

  • Java基础-Bytebuffer

    Bytebuffer 对Bytebuffer的使用和api,这里单独记录一下。包含三个属性: capacity 缓...

  • Java-NIO(3)

    Java-NIO(3) ByteBuffer ByteBuffer为Buffer子类,可以在缓冲区中以字节为单位对...

  • 初识ByteBuffer入门

    前言 ByteBuffer是nio/aio编程所必须掌握的一个数据结构,也是掌握tio所必须要学会的基础只是。设想...

  • java nio

    ByteBuffer 写文件 ByteBuffer读中文文件 ByteBuffer读取普通文件

  • Netty内存分配原理

    1 java NIO的ByteBuffer Bytebuffer分为两种:HeapByteBuffer(堆内内存)...

  • 3点建立对人生的操控感

    01 保持运动习惯 身体与灵魂,总要一个在路上。运动就是灵魂停下来休息最好的机会——身体在路上,灵魂在放松。 信息...

  • 11.15

    Java 中怎么创建 ByteBuffer?1.1 使用allocate()静态方法ByteBuffer buff...

  • Netty(二) ByteBuf

    Netty ByteBuf 是NIO中ByteBuffer的封装,相比JDK ByteBuffer更加易用; 为读...

  • 攻略|如何驾驭你的航拍轨迹?试试“荔枝”吧!

    什么样的航拍才是真正的上帝视角? 想拍出旅行大片的即视感? 那么,从掌握你的飞行轨迹开始吧! 01无人机飞行操控 ...

网友评论

    本文标题:【原创】掌握对 ByteBuffer 的操控感

    本文链接:https://www.haomeiwen.com/subject/appavxtx.html