Buffer是Java NIO中对于缓冲区的封装。在Java BIO中,全部的读写API,都是直接使用byte数组做为缓冲区的,简单直接。可是在Java NIO中,缓冲区这一律念变得复杂,多是对应Java堆中的一块内存,也多是对应本地内存中的一块内存。而byte数组只能用来指定Java堆中的一块内存,因此Java NIO中设计了一个新的缓冲区抽象,涵盖了不一样类型缓冲区,这个抽象就是Buffer。数组
Buffer是Java NIO中对于缓冲区的抽象。是一个用于存储特定基本数据类型的容器。Buffer是特定基本数据类型的线性有限序列。安全
Java有8中基本类型:byte,short,int,long,float,double,char,boolean,除了boolean类型外,其余的类型都有对应的Buffer具体实现:函数
Buffer抽象类定义了全部类型的Buffer都有的属性和操做,属性以下:this
capacity
:缓冲区的容量,在缓冲区创建后就不能改变limit
:表示第一个不能读写的元素位置,limit不会大于capacityposition
:表示下一个要读写的元素位置,position不会大于limitmark
:用于暂存一个元素位置,和书签同样,用于后续操做全部的Buffer操做都围绕这些属性进行。这些属性知足一个不变式:0<=mark<=position<=limit<=capacity
。spa
新建的Buffer这些属性的取值为:操作系统
直接看定义比较抽象,能够看一下示意图,下图是一个容量为10的Buffer:线程
全部Buffer实现中,最重要的实现是ByteBuffer,由于操做系统中全部的IO操做都是对字节的操做。当咱们须要从字节缓冲区中读取别的数据类型才须要使用其余具体类型的Buffer实现。设计
ByteBuffer也是一个抽象类,具体的实现有HeapByteBuffer和DirectByteBuffer。分别对应Java堆缓冲区与堆外内存缓冲区。Java堆缓冲区本质上就是byte数组,因此实现会比较简单。而堆外内存涉及到JNI代码实现,较为复杂,本次咱们以HeapByteBuffer为例来分析Buffer的相关操做,后续专门分析DirectByteBuffer。code
ByteBuffer的类图以下:ip
Buffer做为缓冲区,最主要的做用是用于传递数据。Buffer提供了一系列的读取与写入操做。由于不一样类型的Buffer读写的类型不一样,因此具体的方法定义是定义在Buffer实现类中的。与读写相关的API以下:
1 2 3 4 5 6 7 8 9 |
|
Buffer的读写操做能够按照两种维度分类:
接着咱们来看看这些函数在HeapByteBuffer中是如何实现的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
单字节put与get逻辑同样。看一下批量get是如何实现的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
能够看出,HeapByteBuffer是封装了对byte数组的简单操做。对缓冲区的写入和读取本质上是对数组的写入和读取。使用HeapByteBuffer的好处是咱们不用作各类参数校验,也不须要另外维护数组当前读写位置的变量了。
同时咱们能够看到,Buffer中对于position的操做没有使用锁进行保护,因此Buffer不是线程安全的。
虽然JDK的Java Doc并无提到Buffer有模式,可是Buffer提供了flip等操做用于切换Buffer的工做模式。在正确使用Buffer时,必定要注意Buffer的当前工做模式。不然会致使数据读写不符合你的预期。
Buffer有两种工做模式,一种是接收数据模式,一种是输出数据模式。
新建的Buffer处于接收数据的模式,能够向Buffer放入数据,放入一个对应基本类型的数据后,position加一,若是position已经等于limit了还进行put操做,则会抛出BufferOverflowException异常。
这种模式的Buffer能够用于Channel的read操做缓冲区,或者是用于相对put操做。
好比向一个接受数据模式的Buffer put5个byte后的示例图:
由于Buffer的设计是读写的位置变量都使用position这个变量,因此若是要从Buffer中读取数据,要切换Buffer到输出数据模式。Buffer提供了flip方法用于这种切换。
1 2 3 4 5 6 |
|
切换后的效果图:
而后就能够从Buffer中读取数据了。每次读取一个元素,position就会加一,若是position已经等于limit还进行读取,会抛出BufferUnderflowException异常。
能够看出Buffer自己没有一个用于存储模式的变量,模式的切换只是position和limit的变换而已。
flip方法只会把Buffer从接收模式切换到输出模式,若是要从输出模式切换到接收模式,可使用compact
或者clear
方法,若是数据已经读取完毕或者数据不要了,使用clear
方法,若是已读的数据须要保留,同时须要切换到接收数据模式,使用compat
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
flip
方法能够切换Buffer到输出数据模式。使用compact
或者clear
方法能够切换到接收数据模式。