Buffer简介
Buffer意为缓冲区,其本质上就是是一块可写入数据,然后可以从中读取数据的内存区域,通过该种方式有助于减少系统开销和提高外设效率。对于缓冲区我们早有所了解,比如在C中标准I/O中的read,write直接调用系统的输入输出,而scanf和printf则借助缓冲区在适当的时候调用read,write操作。在NIO中,为了方便对缓冲区的操作,jAVA设计者将缓冲区封装为Buffer(实际上就是封装了基本数据元素的数组),并提供相应的方法对其操作。
在开始之前,首先需要明白以下几个概念:
- 读模式和写模式
- Buffer的capacity
- Buffer的position
- Buffer的limit
读模式和写模式
对于一块内存区而言,将数据写入该区域的过程称之为写模式。反之,称之为读模式。
capacity
该内存的大小即数组的的容量称为Buffer的capacity。一旦Buffer满了,你需要将其清空才能继续写入数据。
position
Buffer当前游标的位置称为position。在写模式下position表示当前的位置。初始的position值为0.当一个byte、long等数据写到Buffer后,position会向前移动到下一个可插入数据的Buffer单元。position最大可为capacity – 1。当将Buffer从写模式切换到读模式,position会先被重置为0,然后当从Buffer的position处读取数据时,position向前移动到下一个可读的位置。
limit
在写模式下,Buffer的limit表示你最多能往Buffer里写多少数据。写模式下,limit等于Buffer的capacity。当切换Buffer到读模式时,limit表示你最多能读到多少数据。因此,当切换Buffer到读模式时,limit会被设置成写模式下的position值。换句话说,你能读到之前写入的所有数据(limit被设置成已写数据的数量,这个值在写模式下就是position)
我们用一张图来大体的概括:
Buffer使用
Buffer的使用过程大体遵循以下步骤:
分配缓存大小——>写数据到Buffer——>调用其filp()——>从Buffer读取数据——>调用clear()或者compact()。
当向buffer写入数据时,buffer会记录下写了多少数据。一旦要读取数据,需要通过flip()方法将Buffer从写模式切换到读模式。在读模式下,可以读取之前写入到buffer的所有数据。一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。
1. 创建Buffer
通过allocate()或者通过wrap()方法
ByteBuffer byteBuffer=ByteBuffer.allocate(48)
Byte[] bytes=new Byte[1024];
//通过该种方式将生成一个limit=capacity=bytes.length的新缓存区,如果bytes里含有数据,则会用该数据填充缓冲区,要注意的是通过该种方式创建的ByteBuffer其position初始值是0.
ByteBuffer byteBuffer=ByteBuffer.wrap(bytes);
2. 向Buffer中写数据
通过Channel的read方法,将数据写到Buffer
通过Buffer自身的put()方法
int bytesRead=fileChannel.read(byteBuffer);
byteBuffer.put(2);
3. 通过filp()进行模式转换
flip方法将Buffer从写模式切换到读模式。调用flip()方法会首先将limit的值设为当前position的值,然后将position设为0,相当执行了以下语句:
limit=position;
position=0;
换句话说,position现在用于标记读的位置,limit表示之前写进了多少个byte、char等 —— 现在能读取多少个byte、char等。
4. 读取Buffer的数据
从Buffer读取数据到Channel
使用Buffer的get()方法读取Buffer的数据
5. 重读buffer中的数据
Buffer.rewind()将position设回0,所以你可以重读Buffer中的所有数据。limit保持不变,仍然表示能从Buffer中读取多少个元素(byte、char等)。
6. 清空Buffer
一旦读完Buffer中的数据,需要让Buffer准备好再次被写入。可以通过clear()或compact()方法来完成。
如果调用的是clear()方法,position将被设回0,limit被设置成 capacity的值。换句话说,Buffer相当于 被清空了(实际上Buffer中的内容并未真正被清空,此时如果调用rewind()或者设置position=0仍然可读取旧的数据)。该方法实际上只是重设了position和limit的值,进而告诉我们可以从哪里开始往Buffer里写数据。
如果Buffer中有一些未读的数据,调用clear()方法,数据将“被遗忘”,意味着不再有任何标记会告诉你哪些数据被读过,哪些还没有。
如果Buffer中仍有未读的数据,且后续还需要这些数据,但是此时想要先先写些数据,那么使用compact()方法。
compact()方法将所有未读的数据拷贝到Buffer起始处。然后将position设到最后一个未读元素正后面。limit属性依然像clear()方法一样,设置成capacity。现在Buffer准备好写数据了,但是不会覆盖未读的数据。
7. 通过mark()标记position位置,在进行其他操作后可通过reset() 方法恢复到标记的position
buffer.mark()
...
buffer.reset()
Buffer完整示例
public class testBuffer {
public static void main(String[] args) {
/**
* 分配空间 隐含地在内存中分配了一个byte型数组来存储10个byte
*/
ByteBuffer buffer = ByteBuffer.allocate(10);
/**
* 填充元素 buffer.hasRemaining()用于判断缓冲区是否达到上界limit。 该填充过程等效于:int remainCount = buffer.remaining();for (int j = 0; j < remainCount;
* j++){buffer.put((byte) j++);}
*/
int i = 0;
while (buffer.hasRemaining()) {
buffer.put((byte) i++);
}
/**
* 翻转缓冲区 将缓冲区进行翻转操作,即在缓冲区写入完毕时,将缓冲区翻转成一个准备读出元素的状态。 flip操作等效于buffer.limit(buffer.position()).position(0);同时将mark设为-1。 源码如下:public final Buffer
* flip(){ limit = position;position = 0;mark = -1;return this;}
*/
buffer.flip();
/**
* 读取缓冲区
*/
int remainCount = buffer.remaining();
for (int j = 0; j < remainCount; j++) {
System.out.print(buffer.get() + " ");
}
System.out.println();
/**
* 字节顺序
*/
System.out.println("ByteOrder的字节顺序为:" + ByteOrder.nativeOrder());
System.out.println("ByteBuffer的字节顺序为:" + buffer.order());
CharBuffer charBuffer = CharBuffer.allocate(10);
System.out.println("CharBuffer的字节顺序为:" + charBuffer.order());
// 修改ButyBuffer的字节顺序
buffer.order(ByteOrder.LITTLE_ENDIAN);
System.out.println("ByteBuffer的字节顺序为:" + buffer.order());
/**
* 只有ByteBuffer可以创建直接缓冲区,用wrap函数创建的缓冲区都是非直接的缓冲区
*/
ByteBuffer redirectByteBuffer = ByteBuffer.allocateDirect(10);
System.out.println("判断缓冲区是否为直接缓冲区:" + redirectByteBuffer.isDirect());
/**
* 先创建一个大端字节顺序的ByteBuffer,然后再创建一个字符视图缓冲区
*/
ByteBuffer bigByteBuffer = ByteBuffer.allocate(7).order(ByteOrder.BIG_ENDIAN);
CharBuffer viewCharBuffer = bigByteBuffer.asCharBuffer();
viewCharBuffer.put("你好啊");
/**
* 在字符视图的基础上创建只读字符视图,只能读而不能写,否则抛出ReadOnlyBufferException
*/
CharBuffer onlyReadCharBuffer = viewCharBuffer.asReadOnlyBuffer();
viewCharBuffer.flip();
System.out.println("asCharBuffer()--->position=" + viewCharBuffer.position() + ",limit=" + viewCharBuffer.limit());
while (viewCharBuffer.hasRemaining()) {
System.out.println((char) viewCharBuffer.get());
}
/**
* 创建一个与原始缓冲区相似的新缓冲区,两个缓冲区共享数据元素,拥有同样的容量,但每个缓冲区拥有各自的位置、上界、标记属性。 对一个缓冲区的数据元素所做的改变会反映在另一个缓冲区上,新的缓冲区会继承原始缓冲区的这些属性。
*/
CharBuffer copyCharBuffer = viewCharBuffer.duplicate();
System.out.println("duplicate()--->position=" + copyCharBuffer.position() + ",limit=" + copyCharBuffer.limit());
copyCharBuffer.position(2);
while (copyCharBuffer.hasRemaining()) {
System.out.println((char) copyCharBuffer.get());
}
/**
* 创建原始缓冲区子集的新缓冲区,新缓冲区的内容将从该缓冲区的当前位置开始。对这个缓冲区的内容做出的改变将在反映在新的缓冲区上,可见,反之亦然;这两个缓冲区的位置、限制和标记值将是独立的。
*/
viewCharBuffer.position(1);
CharBuffer cutCharBuffer = viewCharBuffer.slice();
System.out.println("slice()--->position=" + cutCharBuffer.position() + ",limit=" + cutCharBuffer.limit()+",capacity="+cutCharBuffer.capacity());
while (cutCharBuffer.hasRemaining()) {
System.out.println((char) cutCharBuffer.get());
}
}
}
最新评论
1.4 可以嘛?
可以
2022.06.06版本2022.1 临时激活码激活提示key is invalid 热心大佬提供的激活码里跟文章里的激活码是一样的 坐等lz掘地三尺
我打开绿色版,用配色方案的时候,还是弹出付费
非常好用,谢谢!lz好人一生平安!
感谢楼主
东西不错,希望能够继续保持
这个win版破解之后,就不要激活吧~