和其他java类一样,缓冲区类内部也分为成员变量和方法两个组成部分。在这里,成员变量称之为状态变量,方法称为访问方法,每一个访问方法的执行都会改变状态变量的值。
状态变量
- position :指向缓冲区(底层数组)下一个空闲位置的索引,如缓冲区当前有三个元素,那么position=3,他也同样指明了缓冲区当前可用数据大小。他也是继续向缓冲区写数据和从缓冲区读取数据的开始。
- limit :
变量表明还有多少数据需要取出(在从缓冲区写入通道时),或者还有多少空间可以放入数据(在从通道读入缓冲区时)。
position
总是小于或者等于limit
。 - capacity :
capacity
表明可以储存在缓冲区中的最大数据容量。实际上,它指定了底层数组的大小 — 或者至少是指定了准许我们使用的底层数组的容量。limit
决不能大于capacity
。capacity不会改变,而其他两个状态变量会经常变动
访问方法:会对position和limit产生影响
- flip() :limit=position;position=0;为从缓冲区读取数据做好准备
- clear() :limit = capacity;position=0;回到缓冲区最初始的状态,为往缓冲区写数据做好准备。
- get(): 有四个变种,用于从缓冲区直接读取数据到数组
- put():向缓冲区写入数据
- 和基本类型对应的putXXX和getXXX方法。
以下这个例子描述了从一个缓冲区读取数据写入另一个缓冲区的简单代码:
缓冲区的创建:
- ByteBuffer buffer = ByteBuffer.allocate( 10 ); 底层使用同样大小的数组作为数据结构
byte array[] = new byte[10]; ByteBuffer buffer = ByteBuffer.wrap( array );//使用现有数据存放数据,查看源代码可以发现,被wrap的数据中数据不会清空
缓冲区分片:
slice():返回position和limit直间的数组作为底层的缓冲区,在实质上和原始缓冲区公用底层数据,相当于将缓冲区有效数值的一个视图,对视图的修改会影响底层数据
只读缓冲区:
asReadOnlyBuffer():将常规缓冲区转化为只读缓冲区,还是和原始缓冲区共享底层结构和缓冲区数值
直接和间接缓冲区
另一种有用的 ByteBuffer
是直接缓冲区。 直接缓冲区 是为加快 I/O
速度,而以一种特殊的方式分配其内存的缓冲区。
实际上,直接缓冲区的准确定义是与实现相关的。Sun 的文档是这样描述直接缓冲区的:
给定一个直接字节缓冲区,Java 虚拟机将尽最大努力直接对它执行本机 I/O 操作。也就是说,它会在每一次调用底层操作系统的本机 I/O 操作之前(或之后),尝试避免将缓冲区的内容拷贝到一个中间缓冲区中(或者从一个中间缓冲区中拷贝数据)。
直接缓冲区获得: ByteBuffer buffer = ByteBuffer.allocateDirect( 1024 );
使用和普通缓冲区一样使用
内存映射文件 I/O
内存映射文件 I/O 是一种读和写文件数据的方法,它可以比常规的基于流或者基于通道的 I/O 快得多。
内存映射文件 I/O 是通过使文件中的数据神奇般地出现为内存数组的内容来完成的。这其初听起来似乎不过就是将整个文件读到内存中,但是事实上并不是这样。一般来说,只有文件中实际读取或者写入的部分才会送入(或者 映射 )到内存中。
内存映射并不真的神奇或者多么不寻常。现代操作系统一般根据需要将文件的部分映射为内存的部分,从而实现文件系统。Java 内存映射机制不过是在底层操作系统中可以采用这种机制时,提供了对该机制的访问。
尽管创建内存映射文件相当简单,但是向它写入可能是危险的。仅只是改变数组的单个元素这样的简单操作,就可能会直接修改磁盘上的文件。修改数据与将数据保存到磁盘是没有分开的。
将文件映射到内存 第 8 页(共8 页)
了解内存映射的最好方法是使用例子。在下面的例子中,我们要将一个 FileChannel
(它的全部或者部分)映射到内存中。为此我们将使用 FileChannel.map()
方法。下面代码行将文件的前 1024
个字节映射到内存中:
MappedByteBuffer mbb = fc.map( FileChannel.MapMode.READ_WRITE,
0, 1024 );
map()
方法返回一个 MappedByteBuffer
,它是
ByteBuffer
的子类。因此,您可以像使用其他任何 ByteBuffer
一样使用新映射的缓冲区,操作系统会在需要时负责执行行映射。