ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[TOC] ## 第一章 缓冲区的使用 在NIO技术的缓冲区中,存在4个核心技术点,分别是: ❑capacity(容量) ❑limit(限制) ❑position(位置) ❑mark(标记) ![](https://img.kancloud.cn/86/80/8680a2d4dccdd5673e4c14b229ef9ff5_637x179.png) 这4个技术点之间值的大小关系如下:0≤mark≤position≤limit≤capacity。 由于ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer和ShortBuffer是抽象类,wrap()就相当于创建这些缓冲区的工厂方法。最终对应的类型分别为`java.io.HeapByteBuffer`、`java.io.HeapCharBuffer`、`java.io.HeapDoubleBuffe`等。 ### limit 限制(limit)代表第一个不应该读取或写入元素的index,缓冲区的limit不能为负,并且limit不能大于其capacity。如果position大于新的limit,则将position设置为新的limit。如果mark已定义且大于新的limit,则丢弃该mark。 ![image-20210501144736092](https://gitee.com/cowboy2014/cloud2020-config/raw/master//pictures/image-20210501144736092.png) ```java public class BufferTest { @Test public void bufferLimitTest(){ char[] charArray = new char[]{'a','b','c','d','e'}; CharBuffer buffer = CharBuffer.wrap(charArray); System.out.println("A capacity()=" + buffer.capacity() + ",limit="+buffer.limit()); buffer.limit(3); System.out.println("B capacity()=" + buffer.capacity() + ",limit="+buffer.limit()); buffer.put(0,'o'); buffer.put(1,'p'); buffer.put(2,'q'); buffer.put(3,'r');//index == 3,第一个不可读不可写的索引 buffer.put(4,'s'); buffer.put(5,'t'); buffer.put(6,'u'); } } ``` ### position(位置) 什么是位置呢?它代表“下一个”要读取或写入元素的index(索引),缓冲区的position(位置)不能为负,并且position不能大于其limit。如果mark已定义且大于新的position,则丢弃该mark。 ![image-20210501151228104](https://gitee.com/cowboy2014/cloud2020-config/raw/master//pictures/image-20210501151228104.png) ```java @Test public void bufferLimitTest(){ char[] charArray = new char[]{'a','b','c','d','e'}; CharBuffer buffer = CharBuffer.wrap(charArray); System.out.println("A capacity()=" + buffer.capacity() + ",limit="+buffer.limit()+ ",position="+buffer.position()); buffer.limit(3); System.out.println("B capacity()=" + buffer.capacity() + ",limit="+buffer.limit() + ",position="+buffer.position()); buffer.put(0,'o'); buffer.put(1,'p'); buffer.put(2,'q'); buffer.put(3,'r');//index == 3,第一个不可读不可写的索引 buffer.put(4,'s'); buffer.put(5,'t'); buffer.put(6,'u'); } ``` > 运行结果如下: > A capacity()=5,limit=5,position=0 > B capacity()=5,limit=5,position=2 > a > b > Z > d > e ### mark(标记) 标记有什么作用呢?缓冲区的标记是一个索引,在调用reset()方法时,会将缓冲区的position位置重置为该索引。标记(mark)并不是必需的。定义mark时,不能将其定义为负数,并且不能让它大于position。 - 如果定义了mark,则在将position或limit调整为小于该mark的值时,该mark被丢弃,丢弃后mark的值是-1。 - 如果未定义mark,那么调用reset()方法将导致抛出`InvalidMarkException`异常。 简而言之,buffer的reset()方法,可以根据mark()的位置,把位置重置为mark()调用时的位置。 其他的一些关系: - 如果position大于新的limit,则position的值就是新limit的值。 ### 缓冲区操作 | 方法名 | 方法含义 | 用途| | --- | --- | --- | | clear() | position=0; limit=capacity; mark = null|还原缓冲区| | flip() | limit=positon; position=0; | 先写后读| | rewind() | position=0; mark =null| 重读、重写| - clear(),还原缓冲区。还原缓冲区到初始的状态,包含将位置设置为0,将限制设置为容量,并丢弃标记,即“回到默认”。 - flip()。反转缓冲区。首先将限制设置为当前位置,然后将位置设置为0。为啥不叫做截取有效缓冲区呢?flip()常用在向缓冲区中写入一些数据后,下一步读取缓冲区中的数据之前调用,以改变limit与position的值。 - rewind(),重绕此缓冲区,将位置设置为0并丢弃标记。在重新读取、重新写入时可以使用。 ## 二、缓冲区的内存分配 ### 非直接缓冲区和直接缓冲区 #### 非直接缓冲区 ![image-20210501153630472](https://gitee.com/cowboy2014/cloud2020-config/raw/master//pictures/image-20210501153630472.png) 通过ByteBuffer向硬盘存取数据时是需要将数据暂存在JVM的中间缓冲区,如果有频繁操作数据的情况发生,则在每次操作时都会将数据暂存在JVM的中间缓冲区,再交给ByteBuffer处理,这样做就大大降低软件对数据的吞吐量,提高内存占有率,造成软件运行效率降低,这就是非直接缓冲区保存数据的过程,所以非直接缓冲区的这个弊端就由直接缓冲区解决了。 #### 直接缓冲区 ![image-20210501153705097](https://gitee.com/cowboy2014/cloud2020-config/raw/master//pictures/image-20210501153705097.png)