[TOC] > 在开始之前,我觉得有必要充分理解一下缓冲区的作用及使用方法。 ## 一、缓冲区基础 在NIO技术的缓冲区中,存在4个核心技术点,分别是: ❑capacity(容量) ❑limit(限制) ❑position(位置) ❑mark(标记) 这4个技术点之间值的大小关系如下:0≤mark≤position≤limit≤capacity。 由于ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer和ShortBuffer是抽象类,wrap()就相当于创建这些缓冲区的工厂方法。最终对应的类型分别为`java.io.HeapByteBuffer`、`java.io.HeapCharBuffer`、`java.io.HeapDoubleBuffer`等。 ### limit 限制(limit)代表第一个不应该读取或写入元素的index,缓冲区的limit不能为负,并且limit不能大于其capacity。如果position大于新的limit,则将position设置为新的limit。如果mark已定义且大于新的limit,则丢弃该mark。 ![](https://img.kancloud.cn/34/aa/34aaea743ca4efa39c9942ba0b5e1850_386x153.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。 ![](https://img.kancloud.cn/cf/f6/cff65cd16f6f506d1565fecbd589c82f_267x105.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的值。 ## 二、非直接缓冲区和直接缓冲区 ### 非直接缓冲区 ![](https://img.kancloud.cn/05/17/0517591f49a57e9acf20783377ac5d2c_457x115.png) 通过ByteBuffer向硬盘存取数据时是需要将数据暂存在JVM的中间缓冲区,如果有频繁操作数据的情况发生,则在每次操作时都会将数据暂存在JVM的中间缓冲区,再交给ByteBuffer处理,这样做就大大降低软件对数据的吞吐量,提高内存占有率,造成软件运行效率降低,这就是非直接缓冲区保存数据的过程,所以非直接缓冲区的这个弊端就由直接缓冲区解决了。 ### 直接缓冲区 ![](https://img.kancloud.cn/68/85/68856c6847845e661fccd07bf19c250f_508x127.png)