ByteBuffer前前后后看过好几次了,实际使用也用了一些,总觉得条理不够清晰。

《程序员的思维修炼》一本书讲过,主动学习,要比单纯看资料效果来的好,所以干脆写个详细点的文章来记录一下。

概述

ByteBuffer是NIO里用得最多的Buffer,它包含两个实现方式:HeapByteBuffer是基于Java堆的实现,而DirectByteBuffer则使用了unsafe的API进行了堆外的实现。这里只说HeapByteBuffer。

使用

ByteBuffer最核心的方法是put(byte)get()。分别是往ByteBuffer里写一个字节,和读一个字节。

值得注意的是,ByteBuffer的读写模式是分开的,正常的应用场景是:往ByteBuffer里写一些数据,然后flip(),然后再读出来。

这里插两个Channel方面的对象,以便更好的理解Buffer。

ReadableByteChannel是一个从Channel中读取数据,并保存到ByteBuffer的接口,它包含一个方法:

public intread(ByteBuffer dst) throwsIOException;

 

WritableByteChannel则是从ByteBuffer中读取数据,并输出到Channel的接口:

public intwrite(ByteBuffer src) throwsIOException;

 

那么,一个ByteBuffer的使用过程是这样的:

1. byteBuffer = ByteBuffer.allocate(N);    //创建

2. readableByteChannel.read(byteBuffer);   //读取数据,写入byteBuffer

3. byteBuffer.flip();              //变读为写

4. writableByteChannel.write(byteBuffer);   //读取byteBuffer,写入数据

 

看到这里,一般都不太明白flip()干了什么事,先从ByteBuffer结构说起:

ByteBuffer的创建和读写

1. ByteBuffer定义了4个static方法来做创建工作:

  ByteBuffer allocate(int capacity) //创建一个指定capacity的ByteBuffer。
  ByteBuffer allocateDirect(int capacity) //创建一个direct的ByteBuffer,这样的ByteBuffer在参与IO操作时性能会更好
  ByteBuffer wrap(byte [] array)
  ByteBuffer wrap(byte [] array, int offset, int length) //把一个byte数组或byte数组的一部分包装成ByteBuffer。

2. ByteBuffer定义了一系列get和put操作来从中读写byte数据,如下面几个:
  byte get()
  ByteBuffer get(byte [] dst)
  byte get(int index)
  ByteBuffer put(byte b)
  ByteBuffer put(byte [] src)
  ByteBuffer put(int index, byte b) 
    这些操作可分为绝对定位和相对定为两种,相对定位的读写操作依靠position来定位Buffer中的位置,并在操
  作完成后会更新position的值。在其它类型的buffer中,也定义了相同的函数来读写数据,唯一不同的就是一
  些参数和返回值的类型。

3. 除了读写byte类型数据的函数,ByteBuffer的一个特别之处是它还定义了读写其它primitive数据的方法,如:

  int getInt()             //从ByteBuffer中读出一个int值。
  ByteBuffer putInt(int value)  // 写入一个int值到ByteBuffer中。

  3.1 字节序

    读写其它类型的数据牵涉到字节序问题,ByteBuffer会按其字节序(大字节序或小字节序)写入或读出一个其它
  类型的数据(int,long…)。字节序可以用order方法来取得和设置:
  ByteOrder order() //返回ByteBuffer的字节序。
  ByteBuffer order(ByteOrder bo)   // 设置ByteBuffer的字节序。

  3.2 ByteOrder
    用来表示ByteBuffer字节序的类,可将其看成java中的enum类型。主要定义了下面几个static方法和属性:
    ByteOrder BIG_ENDIAN       代表大字节序的ByteOrder。
    ByteOrder LITTLE_ENDIAN 代表小字节序的ByteOrder。
    ByteOrder nativeOrder()       返回当前硬件平台的字节序。

4. ByteBuffer另一个特别的地方是可以在它的基础上得到其它类型的buffer。如:
  CharBuffer asCharBuffer()
    为当前的ByteBuffer创建一个CharBuffer的视图。在该视图buffer中的读写操作会按照ByteBuffer的字节
  序作用到ByteBuffer中的数据上。

    用这类方法创建出来的buffer会从ByteBuffer的position位置开始到limit位置结束,可以看作是这段数据
  的视图。视图buffer的readOnly属性和direct属性与ByteBuffer的一致,而且也只有通过这种方法,才可
  以得到其他数据类型的direct buffer。

ByteBuffer内部字段

byte[] buff

buff即内部用于缓存的数组。

position

当前读取的位置。

读/写操作的当前下标。当使用buffer的相对位置进行读/写操作时,读/写会从这个下标进行,并在操作完成后,
buffer会更新下标的值。

mark

为某一读过的位置做标记,便于某些时候回退到该位置。

一个临时存放的位置下标。调用mark()会将mark设为当前的position的值,以后调用reset()会将position属性设
置为mark的值。mark的值总是小于等于position的值,如果将position的值设的比mark小,当前的mark值会被抛弃掉。

capacity

初始化时候的容量。

这个Buffer最多能放多少数据。capacity一般在buffer被创建的时候指定。

limit

在Buffer上进行的读写操作都不能越过这个下标。当写数据到buffer中时,limit一般和capacity相等,当读数据时,
limit代表buffer中有效数据的长度。

读写的上限,limit<=capacity。

这些属性总是满足以下条件:
  0 <= mark <= position <= limit <= capacity

limit和position的值除了通过limit()和position()函数来设置,也可以通过下面这些函数来改变:

Buffer clear()
  把position设为0,把limit设为capacity,一般在把数据写入Buffer前调用。

Buffer flip()
  把limit设为当前position,把position设为0,一般在从Buffer读出数据前调用。

Buffer rewind()
  把position设为0,limit不变,一般在把数据重写入Buffer前调用。

compact()

  该方法的作用是将 position 与 limit之间的数据复制到buffer的开始位置,复制后 position  = limit -position,limit = capacity

  但如果position 与limit 之间没有数据的话发,就不会进行复制  详细参考:java nio Buffer 中 compact的作用

mark()与reset()方法

  通过调用Buffer.mark()方法,可以标记Buffer中的一个特定position。之后可以通过调用Buffer.reset()方法恢复到这个position。例如:

  1.buffer.mark();

  2.//call buffer.get() a couple of times, e.g. during parsing.

  3.buffer.reset(); //set position back to mark

equals()与compareTo()方法

  可以使用equals()和compareTo()方法两个Buffer。

  equals()

  当满足下列条件时,表示两个Buffer相等:

  1. 有相同的类型(byte、char、int等)。
  2. Buffer中剩余的byte、char等的个数相等。
  3. Buffer中所有剩余的byte、char等都相同。

  如你所见,equals只是比较Buffer的一部分,不是每一个在它里面的元素都比较。实际上,它只比较Buffer中的剩余元素。

  compareTo()方法

  compareTo()方法比较两个Buffer的剩余元素(byte、char等), 如果满足下列条件,则认为一个Buffer“小于”另一个Buffer:

    1. 第一个不相等的元素小于另一个Buffer中对应的元素 。
    2. 所有元素都相等,但第一个Buffer比另一个先耗尽(第一个Buffer的元素个数比另一个少)。

Buffer对象有可能是只读的,这时,任何对该对象的写操作都会触发一个ReadOnlyBufferException。
isReadOnly()方法可以用来判断一个Buffer是否只读。

图解

put

写模式下,往buffer里写一个字节,并把postion移动一位。写模式下,一般limit与capacity相等。

flip

写完数据,需要开始读的时候,将postion复位到0,并将limit设为当前postion。

get

从buffer里读一个字节,并把postion移动一位。上限是limit,即写入数据的最后位置。

clear

将position置为0,并不清除buffer内容。

mark相关的方法主要是mark()(标记)和reset()(回到标记).

这篇文章对buffer的讲解也很详细,可以参考 Java NIO系列教程(三) Buffer

其他具体的接口信息查阅 http://docs.oracle.com/javase/7/docs/api/java/nio/ByteBuffer.html

搞懂iobuffer就得先学习bytebuffer的更多相关文章

  1. 一天搞懂深度学习-训练深度神经网络(DNN)的要点

    前言 这是<一天搞懂深度学习>的第二部分 一.选择合适的损失函数 典型的损失函数有平方误差损失函数和交叉熵损失函数. 交叉熵损失函数: 选择不同的损失函数会有不同的训练效果 二.mini- ...

  2. 完全搞懂傅里叶变换和小波(1)——总纲<转载>

    无论是学习信号处理,还是做图像.音视频处理方面的研究,你永远避不开的一个内容,就是傅里叶变换和小波.但是这两个东西其实并不容易弄懂,或者说其实是非常抽象和晦涩的! 完全搞懂傅里叶变换和小波,你至少需要 ...

  3. java线程间通信:一个小Demo完全搞懂

    版权声明:本文出自汪磊的博客,转载请务必注明出处. Java线程系列文章只是自己知识的总结梳理,都是最基础的玩意,已经掌握熟练的可以绕过. 一.从一个小Demo说起 上篇我们聊到了Java多线程的同步 ...

  4. (转)从一道面试题彻底搞懂hashCode与equals的作用与区别及应当注意的细节

    背景:学习java的基础知识,每次回顾,总会有不同的认识.该文系转载 最近去面试了几家公司,被问到hashCode的作用,虽然回答出来了,但是自己还是对hashCode和equals的作用一知半解的, ...

  5. 真正“搞”懂http协议01—背景故事

    去年读了<图解HTTP>.<图解TCP/IP>以及<图解网络硬件>但是读了之后并没有什么深刻的印象,只是有了一层模糊的脉络,刚好最近又接触了一些有关http的相关内 ...

  6. 基础篇|一文搞懂RNN(循环神经网络)

    基础篇|一文搞懂RNN(循环神经网络) https://mp.weixin.qq.com/s/va1gmavl2ZESgnM7biORQg 神经网络基础 神经网络可以当做是能够拟合任意函数的黑盒子,只 ...

  7. c#代码 天气接口 一分钟搞懂你的博客为什么没人看 看完python这段爬虫代码,java流泪了c#沉默了 图片二进制转换与存入数据库相关 C#7.0--引用返回值和引用局部变量 JS直接调用C#后台方法(ajax调用) Linq To Json SqlServer 递归查询

    天气预报的程序.程序并不难. 看到这个需求第一个想法就是只要找到合适天气预报接口一切都是小意思,说干就干,立马跟学生沟通价格. ​ ​不过谈报价的过程中,差点没让我一口老血喷键盘上,话说我们程序猿的人 ...

  8. 机器学习:让我们彻底搞懂CNN【转】

    本文转载自:http://115.com/182920/T1266078.html 机器学习:让我们彻底搞懂CNN 上世纪科学家们发现了几个视觉神经特点,视神经具有局部感受眼,一整张图的识别由多个局部 ...

  9. 搞懂分布式技术28:微服务(Microservice)那点事

    搞懂分布式技术28:微服务(Microservice)那点事 微服务(Microservice)那点事 肥侠 2016-01-13 09:46:53 浏览58371 评论15 分布式系统与计算 微服务 ...

随机推荐

  1. OpenGL中VA,VAO,VBO和EBO的区别

    1,顶点数组(Vertex Array) VA,顶点数组也是收集好所有的顶点,一次性发送给GPU.不过数据不是存储于GPU中的,绘制速度上没有显示列表快,优点是可以修改数据. 4.VBO(Vertex ...

  2. 以编程方式使用 Microsoft Office Visio 2003 ActiveX 控件

    以编程方式使用 Microsoft Office Visio 2003 ActiveX 控件 2007/10/29 Mark BukovecEmpire Down Development 适用于:Mi ...

  3. GitHub网站使用的基础入门

    github基本入门,首先需要掌握一些ssh非对称加密的知识和一些基本的git命令操作.下面是我推荐的两个比较好的网站,然后我再专门对GitHub网站使用进行步骤讲解. git 命令:http://b ...

  4. shell编程规范:引用

    Shell代码规范 作 者: 毕小朋 用 途: 规范Shell代码书写,方便查看与修改 博 客: http://blog.csdn.net/wirelessqa 参 考: http://www.ohl ...

  5. Maven可以使用mvn package指令对项目进行打包,如果使用Java -jar xxx.java

    Maven可以使用mvn package指令对项目进行打包,如果使用Java -jar xxx.jar执行运行jar文件,会出现"no main manifest attribute, in ...

  6. 易语言Dns缓存

    一些与DNS解析有关的命令: ipconfig/displaydns   -查看被缓存的域名解析 ipconfig/flushdns     -清空DNS缓存 .版本 .DLL命令 DnsFlushR ...

  7. jQuery-4.动画篇---淡入淡出效果

    jQuery中淡出动画fadeOut 让元素在页面不可见,常用的办法就是通过设置样式的display:none.除此之外还可以一些类似的办法可以达到这个目的.这里要提一个透明度的方法,设置元素透明度为 ...

  8. gmail及youtube

    墙内访问youtube没有特别好的方法,只找到一个,提供格式转换和下载: http://new.cloudfile.co/transfer 1. 修改host文件,照常用网页访问.有人会提供最新hos ...

  9. D1——初读《Head First Java》

    今天随便看了点<Head First Java>,发觉这本书的风格真是有趣.打算先把这本书踏踏实实的看了.学习切忌好高骛远.心浮气躁,尤其入门基础阶段更应该踏踏实实地学习知识.下面随便谈谈 ...

  10. Day8 linux软件包管理

    软件包的两种形式      qq.tar.gz  (需要编译 源码翻译成二进制)/ rpm (直接安装) rpm的文件名分为5部分 name名称  version版本编号  release发布次数  ...