Java 自从 JDK1.4 起,对各种 I/O 操作使用了 Buffer 和 Channel 技术。这种更接近于操作系统的的底层操作使得 I/O 操作速度得到大幅度提升,下面引用一段《Java 编程思想》对于 Buffer(缓冲器)和 Channel 的形象化解释。
我们可以将它想象成一个煤矿,Channel(通道)是一个包含煤层(数据)的矿藏,而 Buffer(缓冲器)则是派送到矿藏的卡车。卡车满载煤炭而归,我们再从卡车上获得煤炭。也就是说,我们并没有直接和 Channel 交互;我们只是和 Buffer 交互,并把 Buffer 派送到 Channel。Channel 要么从 Buffer 获得数据,要么向 Buffer 发送数据。
本片博文简单的介绍一下 java.nio 包中的 Buffer 抽象类。
一、类的介绍
(一)概述
首先 Buffer 是一个能存储基本数据类型的容器(除了 Boolean 类型),从 java.nio 包的继承结构就能看出来。

但是它的子类没有 BooleanBuffer 这样一个类。
Buffer 是一个线性、有序的基本类型元素组成的序列。 除了其中存储的元素,Buffer 类还有 4 个重要的成员属性:
- capacity:它表示一个 Buffer 包含的元素数量,它是非负且恒定不变的。
 - position:它是下一个要读或者写的元素的索引,它是非负的且不会超过 limit 的大小。
 - limit:它是可以读或者写的最后一个元素的索引,它是非负的且不会超过 capacity 的大小。
 - mark:当调用 reset() 方法被调用时,一个 Buffer 的 mark 值会被设定为当前的 position 值的大小。
 
综合上面,可以得出这样一个永远成立的公式:0 <= mark <= position <= limit <= capacity
通过这 4 个属性,间接的实现了 Buffer 中的读写保护机制。
一个新创建的 Buffer 具有以下几个性质:
- 它的 position 是 0;
 - mark 没有被定义(实际上是 -1);
 - 而 limit 值可能是 0,也可能是其他值,这取决于这个 Buffer 的类型;
 - Buffer 中每一个元素值都被初始化为 0
 
(二)数据传输
Buffer 类的每一个子类都有两套 put 和 get 操作,以便向 Buffer 中写数据,或从 Buffer 中读数据。
- (Relative opetations)相对操作:这种操作会从当前的 position 位置开始读写一个或多个元素,position 会按照读写的元素个数值增加。
 - (Absolute opetations)绝对操作:绝对操作要指明读写的元素索引,读写元素后,当前 Buffer 的 position 值不变。
 
上面说的是我们直接与 Buffer 打交道的方式。开篇就说过,Channel 也能操作 Buffer 中的数据,要注意的是这个 Channel 是与当前 Buffer 的 position 位置相关联的 Channel。
(三)一些重要的方法
在学习这些方法前先来看一张图:

因为在创建 Buffer 的时候并不会区分创建的是读 Buffer 还是写 Buffer,所以就需要一种机制来区分当前 Buffer 正在被读还是正在被写,即所谓的读模式与写模式。
利用 position 和 limit 这两个属性就能很好的做出这种区分,从上面的图就能看出来,当 limit = capacity 的时候说明可以向当前 Buffer 写入数据;当二者不等时就可以从 Buffer 中读取数据。
每次读写模式的转换 position 都被重置为 0,下面的 3 个方法就是完成读写模式转换的 3 种途径。
- clear( ) :调用 clear( ) 方法后,我们就可以向 Buffer 里面 put 数据,或者 Channel 能读取 Buffer 里的数据,并且将 limit 设置为 capacity,将 position 设置为 0。
 - flip( ) :调用 flip( ) 方法后,我们就可以向 Buffer 里面 get 数据,或者 Channel 能向 Buffer 里写数据,并且将 limit 设置为 position,将 position 设置为 0。
 - rewind( ) :调用 flip( ) 方法后,使 Buffer 里的数据可以被重新读取(无论是我们从 Buffer 读,还是 Channel 从 Buffer 读),此时 limit 不变,将 position 设置为 0。
 
(四)Buffer 的读写性
Buffer 分为:① 只读 Buffer ② 读写 Buffer
每个 Buffer 都是可读的,但是不是每个 Buffer 都是可写的。
只读 Buffer 的内容是不能变化,但是 mark,limit,position 值是可变的。
(五)线程安全性
在多线程操作下 Buffer 是不安全的,所以在多线程操作同一个 Buffer 的时候要使用相应的线程同步操作。
(六)Buffer 的数组形式
@ TODO
二、类的源代码
public abstract class Buffer { /** * The characteristics of Spliterators that traverse and split elements * maintained in Buffers.(这个属性我目前还没弄懂是干嘛用的) */ static final int SPLITERATOR_CHARACTERISTICS = Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED; // 注意只有 mark 和 position 有初始值 private int mark = -1;

