Buffer
简介 Buffer
的核心属性 Buffer
的创建与使用( ByteBuffer
为例) 缓冲区( Buffer
):本质上是一个数组,用于临时保存、写入以及读取数据。在 Java NIO
中,
该内存块包含在 NIO Buffer
对象当中, NIO Buffer
对象还提供了一组接口来访问该内存块。
根据数据类型的不同, Java
为除了 boolean
类型之外的其余7种基本类型提供了相应类型的缓冲区,
分别是 ByteBuffer
、 CharBuffer
、 ShortBuffer
、 IntBuffer
、 LongBuffer
、
FloatBuffer
、 DoubleBuffer
。他们都继承自抽象类 Buffer
类,他们的管理方式也都几乎一样。
UML
类图如下:
BUffer
类的部分实现如下:
public abstract class Buffer { // Invariants: mark <= position <= limit <= capacity private int mark = -1; private int position = 0; private int limit; private int capacity; //构造方法 Buffer(int mark, int pos, int lim, int cap) { // package-private if (cap < 0) throw new IllegalArgumentException("Negative capacity: " + cap); this.capacity = cap; limit(lim); position(pos); if (mark >= 0) { if (mark > pos) throw new IllegalArgumentException("mark > position: (" + mark + " > " + pos + ")"); this.mark = mark; } } /** * Returns this buffer's capacity. * * @return The capacity of this buffer */ //返回这个Buffer的容量 public final int capacity() { return capacity; } /** * Returns this buffer's position. * * @return The position of this buffer */ //返回这个Buffer中当前的位置(当前操作数) public final int position() { return position; } /** * Returns this buffer's limit. * * @return The limit of this buffer */ //返回当前Buffer中可以被操作的元素的个数 public final int limit() { return limit; } /** * Sets this buffer's mark at its position. * * @return This buffer */ //记录当前position的位置 public final Buffer mark() { mark = position; return this; } public final Buffer reset() { int m = mark; if (m < 0) throw new InvalidMarkException(); position = m; return this; } }
其中定义了四个 Buffer
属性,对应的描述如下
属性 | 描述 |
---|---|
capacity | 容量;用于描述这个Buffer大小,即创建的数组的长度,一旦声明不可以被改变 |
position | 位置,表示当前缓冲区中正在操作的数据的位置,在切换读取时会将其置0 |
limit | 界限、限制;表示当前缓冲区中可以操作的数据的大小,默认情况下为Buffer的大小,切换为读取模式后为数组中元素的个数(准确的说时切换之前position的值) |
mark | 标记;用于记录当前position的位置,后续操作过程中可以使用reset()方法将position还原至最后一次mark的位置 |
在 Java NIO
中可以使用对应 Buffer
类的 allocate()
或者 allocateDirect()
静态方法创建。
//使用allocate()创建 ByteBuffer byteBuffer=ByteBuffer.allocate(1024); //使用allocateDirect()创建 ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
而 Buffer
的本质是一个数组,创建时需要指定数组的大小
Buffer
的使用一般分为四个步骤
Buffer Buffer Buffer Buffer
//使用put()方法向Buffer中写入数据 byteBuffer.put("bmilk".getBytes()); //使用Channel#read()向Buffer中写入数据 channel.read(byteBuffer);
Buffer
切换为读取模式 可以通过调用 flip()
方法将 Buffer
从写模式切换到读模式。
byteBuffer.flip()
调用 flip()
方法会将 position
设回0,并将 limit
设置成之前 position
的值。
即,现在使用 position
标记读的位置, limit
表示之前写进了多少个 byte
,也就是现在
能读取多少个 byte
等。
3. 读取 Buffer
读取 Buffer
有两种方式:
Buffer
种读取数据到 Channel
get()
方法从 Buffer
种读取数据 //从Buffe中将数据写入通道 inChannel.write(byteBuffer) //使用get()方法从BUffer中读取数据 byte[] bytes=new byte[byteBuffer.limit()]; byteBuffer.get(bytes);
4. 将 Buffer
清空,供后续写入使用
使用 clear()
清空缓冲区,清空缓冲区只是使各个指针恢复初始位置,
更具体的说是 position
设置为0, limit
设置为容量的初始大小。
并不会真实清空其中数据,但是可以通过后续的写覆盖之前的数据
byteBuffer.clear()
rewind()
从 Buffer
重复读取数据 //使用`rewind()`从`Buffer`重复读取数据 //Buffer.rewind()将position设回0,所以你可以重读Buffer中的所有数据。 //limit保持不变,仍然表示能从Buffer中读取多少个元素(byte、char等)。 Buffer rewind = byteBuffer.rewind();
compact()
方法 clear()
会使使各个指针恢复初始位置,但是实际中可能存在部分数据还没有被使用,而后续需要使用。
又必须清理一部分 Buffer
的空间, compact()
方法会将所有未读数据拷贝到Buffer的起始处,
然后将 position
指针设置到最后一个未读元素的后面,现在 Buffer
可以进行写数据,
但是不会覆盖前面的未读的数据。
mark()
方法与 reset()
方法 通过调用Buffer.mark()方法,可以标记Buffer中的当前的position。之后可以通过调用Buffer.reset()方法恢复到这个position。
//使用mark标记当前的position位置 byteBUffer.mark() //使用reset方法使position指针返回这个位置 byteBuffer.reset()
4. equals()
方法与 compareTo()
方法
当需要比较两个 Buffer
时可以使用 equals()
方法与 compareTo()
方法。
equals()
方法判断两个方式是否相等,当满足下列条件时,表示两个 Buffer
相等
byte
、 char
、 int
等) Buffer
中剩余的 byte
、 char
等的个数相等。 compareTo()
方法比较两个两个 Buffer
的大小,仅比较剩余元素( byte
、 char
等)
如果满足下列条件,则认为一个 Buffer
“小于”另一个 Buffer
:
allocate() allocateDirect()
非直接缓冲区数据流向图
直接缓冲区数据流向图
直接缓冲区(物理内存映射文件):相比非直接缓冲区省略了 copy
的过程,所以说直接缓区可以一定程度上提高效率
弊端:
java
程序将数据写入物理内存映射文件中,之后数据将不受 Java
程序控制, java
虚拟机会见最大努力直接在此缓冲区上执行本机 I/O
。 I/O
之前或之后,虚拟机都回尽量避免将缓冲区的内容复制到中间缓冲区或者从中间缓冲区中复制内容。 allocateDirect()
工厂方法来创建, FileChannel
的 map()
方法,将文件区域直接映射到内存中来创建, MappedByteBuffer
。 Java
的实现有助于 JNI
从本地及代码创建直接字节缓冲区, isDirect()
方法来确定,提供此方法是为了能够在性能关键型代码中执行显式缓冲区管理。 本文简单介绍了 Buffer
的种类,并对常用方法进行乐简单的介绍
Java NIO系列教程(三) Buffer