【NIO】NIO三剑客之一ByteBuffer介绍与使用

  • ​谈及Java NIO,最核心的三个组件就是
    • Channel 通道
    • ByteBuffer 读写缓冲区
    • Selector 多路复用器
  • 核心之一就是在对ByteBuffer的读写操作上。

ByteBuffer为NIO中的字节缓冲区,相对于BIOStream流只支持写入或者读取单向操作,ByteBuffer是双向的,支持读和写。

  • 类型

    • DirectByteBuffer
      • 使用的是操作系统级别的内存,分配比较慢,但是数据的读写比较快,因为少了一次从系统内存到JVM内存的复制过程
      • 初始化方法:
        • ByteBuffer.allocateDirect(1024 * 4);
    • HeapByteBuffer
      • 使用的是JVM的堆内存,对于JVM来说,分配比较快,但是读写比较慢,因为需要将操作系统内存里的数据复制到JVM内存
      • 初始化方法:
        • ByteBuffer.allocate(1024 * 4);
  • 核心属性

    • capacity
      • ByteBuffer的容量,这个值在ByteBuffer初始化的时候就确定下来了。不论是在读还是在写模式下,这个值都不变。
    • position
      • 写模式下:
        • 该值表示当前写到了ByteBuffer的哪个位置,ByteBuffer初始化时,这个值为0。
        • position的最大值为capacity-1
      • 读模式下:
        • 当从写模式切换到读模式,会将position重置为0,即从ByteBuffer的起始位置开始读取数据。
    • limit
      • 写模式下:
        • limit为最大可写入的数据量,即ByteBuffer的最大容量,值为capacity
      • 读模式下:
        • 当从写模式切换从读模式,limit将会被设置为读模式下的position值,即可读取的最大数据量。
  • 核心方法

    • flip()
      • 将写模式切换为读模式
      • 会触发的对核心属性的操作:
        • position设置为0,即从ByteBuffer起始位置开始读。
        • limit设置为写模式下position的值,即最大可读取的数据量大小。
    • mark()
      • 标记当前position位置
    • reset()
      • position指向上一次mark()所指向的位置,可以从这个位置重复向下读取数据
    • clear()
      • 在逻辑上清空ByteBuffer里的数据,实际上不清空数据
      • 会触发的动作:
        • limit设置为capacity
        • position指向起始位置0
        • 提示:实际上数据并未清理,只是下次是从0的位置开始写入数据,效果上像是数据清空了。
        • 提示:如果ByteBuffer中的数据并未完全读完,调用这个方法将忽略那些未读取的数据。
    • compact()
      • 如果并未读取完ByteBuffer中的数据,调用compact()会将position~limit之间的数据拷贝到ByteBuffer的起始处,并且position为剩余数据量的大小,下次再往ByteBuffer中写入数据时,将在position位置继续往下写,不会覆盖历史数据。
    • hasRemaining()
      • 判断缓冲区中是否还有未读数据
  • 将数据写入ByteBuffer的方式

    • byteBuffer.put(x)
    • channel.read(byteBuffer)
  • 从ByteBuffer中读取数据的方式

    • byteBuffer.get()
    • channel.write(bytebuffer)

# 代码演示

/**
 * ByteBuffer的代码演示
 *
 * @author futao
 * @date 2020/7/7
 */
public class ByteBufferDemo {
    public static void main(String[] args) {
        // ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024 * 4);
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 4);

        // Q: 初始化之后,这三个值分别是多少呢?
        System.out.println("position: " + byteBuffer.position());
        System.out.println("capacity: " + byteBuffer.capacity());
        System.out.println("limit: " + byteBuffer.limit());

        //向ByteBuffer写入数据
        byteBuffer.put("hello, 喜欢天文的pony站长~".getBytes(Constants.CHARSET));

        // Q: 向ByteBuffer中写入数据之后,哪些值会发生变化呢?
        System.out.println(StringUtils.repeat("=", 10) + "写入数据之后" + StringUtils.repeat("=", 10));
        System.out.println("position: " + byteBuffer.position());
        System.out.println("capacity: " + byteBuffer.capacity());
        System.out.println("limit: " + byteBuffer.limit());

        //将ByteBuffer从写模式调整为读模式
        byteBuffer.flip();

        //将ByteBuffer从写模式调整为读模式之后,ByteBuffer的哪些值会发生变化?
        System.out.println(StringUtils.repeat("=", 10) + "调整ByteBuffer为读模式之后" + StringUtils.repeat("=", 10));
        System.out.println("position: " + byteBuffer.position());
        System.out.println("capacity: " + byteBuffer.capacity());
        System.out.println("limit: " + byteBuffer.limit());

        //从ByteBuffer中读取一个字节
        byteBuffer.get();
        // Q: 从ByteBuffer中读取一个字节之后,哪些值会发生变化?
        System.out.println(StringUtils.repeat("=", 10) + "从ByteBuffer中读取一个字节之后" + StringUtils.repeat("=", 10));
        System.out.println("position: " + byteBuffer.position());
        System.out.println("capacity: " + byteBuffer.capacity());
        System.out.println("limit: " + byteBuffer.limit());

        // 记录一个标记
        byteBuffer.mark();
        // 继续往下读取
        byteBuffer.get();
        System.out.println(StringUtils.repeat("=", 10) + "记录一个标记之后继续往下读取" + StringUtils.repeat("=", 10));
        System.out.println("position: " + byteBuffer.position());
        System.out.println("capacity: " + byteBuffer.capacity());
        System.out.println("limit: " + byteBuffer.limit());

        //重置position到上一次mark()的标记位置
        byteBuffer.reset();
        System.out.println(StringUtils.repeat("=", 10) + "reset之后" + StringUtils.repeat("=", 10));
        System.out.println("position: " + byteBuffer.position());
        System.out.println("capacity: " + byteBuffer.capacity());
        System.out.println("limit: " + byteBuffer.limit());

        System.out.println("byteBuffer中是否还有数据:" + byteBuffer.hasRemaining());
        //拷贝未读取的数据到缓冲区最前面
        byteBuffer.compact();
        System.out.println(StringUtils.repeat("=", 10) + "compact之后" + StringUtils.repeat("=", 10));
        System.out.println("position: " + byteBuffer.position());
        System.out.println("capacity: " + byteBuffer.capacity());
        System.out.println("limit: " + byteBuffer.limit());

        //逻辑上清空数据=>实际上只是指针的变化
        byteBuffer.clear();
        System.out.println(StringUtils.repeat("=", 10) + "clear之后" + StringUtils.repeat("=", 10));
        System.out.println("position: " + byteBuffer.position());
        System.out.println("capacity: " + byteBuffer.capacity());
        System.out.println("limit: " + byteBuffer.limit());
    }
}
  • 先不要看结果,自己思考一下哟,逻辑还是很清晰的~
image.png

是不是与你思考的一致~

# 源代码

# 系列文章

欢迎在评论区留下你看文章时的思考,及时说出,有助于加深记忆和理解,还能和像你一样也喜欢这个话题的读者相遇~

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