Buffer
简介Buffer
的核心属性Buffer
的建立与使用(ByteBuffer
为例)缓冲区(Buffer
):本质上是一个数组,用于临时保存、写入以及读取数据。在Java NIO
中,
该内存块包含在NIO Buffer
对象当中,NIO Buffer
对象还提供了一组接口来访问该内存块。java
根据数据类型的不一样,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
属性,对应的描述以下app
属性 | 描述 |
---|---|
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
的本质是一个数组,建立时须要指定数组的大小this
Buffer
的使用通常分为四个步骤spa
Buffer
中写入数据Buffer
切换为读取模式Buffer
Buffer
清空,供后续写入使用1. 写如数据操作系统
//使用put()方法向Buffer中写入数据 byteBuffer.put("bmilk".getBytes()); //使用Channel#read()向Buffer中写入数据 channel.read(byteBuffer);
2. 将Buffer
切换为读取模式指针
能够经过调用flip()
方法将Buffer
从写模式切换到读模式。code
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
等的个数相等。- \(\color{#FF3030}{`Buffer`中全部剩余的`byte`、`char`等都相同}\)
compareTo()
方法比较两个两个Buffer
的大小,仅比较剩余元素(byte
、char
等)
若是知足下列条件,则认为一个Buffer
“小于”另外一个Buffer
:
- 第一个不相等的元素小于另外一个Buffer中对应的元素
- 全部元素都相等,但第一个Buffer比另外一个先耗尽(第一个Buffer的元素个数比另外一个少)。
allocate()
方法分配缓冲区,将缓冲区创建在JVM内存中allocateDirect()
方法分配直接缓冲区,将缓冲区创建在物理内存中,能够在某些状况下提升效率直接缓冲区(物理内存映射文件):相比非直接缓冲区省略了copy
的过程,因此说直接缓区能够必定程度上提升效率
弊端:
java
程序将数据写入物理内存映射文件中,以后数据将不受Java
程序控制,java
虚拟机会见最大努力直接在此缓冲区上执行本机I/O
。I/O
以前或以后,虚拟机都回尽可能避免将缓冲区的内容复制到中间缓冲区或者从中间缓冲区中复制内容。allocateDirect()
工厂方法来建立,FileChannel
的map()
方法,将文件区域直接映射到内存中来建立,MappedByteBuffer
。Java
的实现有助于JNI
从本地及代码建立直接字节缓冲区,isDirect()
方法来肯定,提供此方法是为了可以在性能关键型代码中执行显式缓冲区管理。本文简单介绍了Buffer
的种类,并对经常使用方法进行乐简单的介绍