Java buffer为什么要用flip()和clear()方法

本文详细解析了ByteBuffer中position和limit的概念,阐述了它们在写入和读取数据过程中的作用及变化,介绍了allocate、flip、compact和clear等方法的使用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Bytebuffer:
 position和limit两个变量最重要:
 在写的时候position表示开始写的位置,limit表示能写的最大位置。
 在读的时候position表示开始读取的位置,limit用来表示能够读的最大位置。
 但是读的position和写的position是同一个变量,limit也是所以写完之后读,和读完之后写都需要手动重置这两个变量
 所以这两个变量的变化是半自动的:

 1):在allocate(buffer刚分配好空间或者初始化的时候)的时候,position=0;limit=capacity(分配的容量)。


 2): 在写的时候,刚创建一个buffer,当然要先用来写入一些东西。那么所有的写入操作,都会改变position的位置,position就表示了下一个要写入的位置(是内部一个数组的下标),也表示了已经写入的个数。而limit则不会变化。而position写到limit大小的时候就不会再写入了。当然是否到达需要自己调用remain()判断,不然就异常。


 3):在读的时候,写过的数据当然要开始读了,但是这个时候,position指向的是下一个要写入的位置,limit指向的是最大容量。我们要读取当然要从0开始读,读到我们最后一个写入的位置。这个时候就需要手动修改position的值,让position=0;我们还要记下我们最后一个写入的位置,那么在positon=0之前,让limit=position,这样,position就是开始读取的位置,limit就是能够读的最大位置。而buffer提供了这样一个方法flip(),源码里就写了这两句:limit=position;position=0;
   接着开始读取,每次读取都会让position位置发生改变,直到读取到limit位置。需要自己调用remain()判断,不然报异常。


 4):好了,读取过数据我们又需要存储数据了,这就有个问题,如果上次读取没有读取完怎么办?这个烧脑的问题的答案,当然是建议每次都读取完,不要给自己添麻烦。

        当然如果真的不能读取完,那么我们需要将剩余的数据移到buffer的position=0的位置,buffer也提供了这么一个方法compact();会将剩余数据移动到buffer起始处,自动重置position和limit,使position等于剩余字节数,limit等于最大容量。


    如果是全部读取完,接着我们又要往buffer存数据,这个时候position是上次读取的位置,limit是最大可读取位置,全部读取完成position=limit。那么直接往buffer写,就会出错,因为posiont必须小于limit。而我们也想从0位置开始存储,并且需要最大存储限制limit应该是容量capacity。那我们就要手动修改position和limit的数值。让position=0;而limit=capacity;buffer也提供了这个方法clear();

    总结:创建一个buffer------>存储数据------->调用flip()
                                                      /\                      |
                                                      |         循环       |
                                                      |                       \/
                                             调用clear()<------读取数据

          当然这是个逻辑清晰的基本操作。掌握了limit,position的含义,理解他们需要手动来改变,你还可以做更多的操作。下载源码,硬着头皮去看,会发现源码注释里面讲的真清晰,图也画的很漂亮。

需要注意的是:tcp框架Mina和Netty中,Mina最终使用了java nio-buffer,所以操作和上述基本一致。Netty使用的不是java nio-buffer,所以不需要以上操作,netty的buffer中读和写的positon和limit是分开的。

### Java 缓冲区(Buffer)的使用教程与解决方案 Java NIO 中的缓冲区(Buffer)是用于存储数据的临时容器,主要用于与通道(Channel)进行数据交互。Buffer 的设计使得数据的读写更加高效,并支持多种数据类型的操作。以下是关于 Java Buffer 的详细使用教程与解决方案。 #### Buffer 的基本概念 在 Java NIO 中,缓冲区是数据的临时存储结构,用于在 I/O 操作中作为数据的中转站。Buffer 主要有以下几个核心字段: - **capacity**:缓冲区的容量,表示该 Buffer 最多可以存储多少数据。 - **position**:当前的位置指针,表示下一个要读取或写入的数据的位置。 - **limit**:表示缓冲区中可以操作的数据大小,即可以写入或读取的上限。 - **mark**:标记位置,可以通过 `mark()` 方法设置,之后可以通过 `reset()` 方法恢复到该位置。 这些字段在数据读写过程中起着关键作用,控制着 Buffer 的状态数据流向 [^2]。 #### Buffer 的常见类型 Java NIO 提供了多种 Buffer 类型,分别对应不同的数据类型: - `ByteBuffer` - `CharBuffer` - `DoubleBuffer` - `FloatBuffer` - `IntBuffer` - `LongBuffer` - `ShortBuffer` - `MappedByteBuffer`(用于内存映射文件操作) 其中,`ByteBuffer` 是最常用的 Buffer 类型,常用于网络通信文件 I/O 操作 [^3]。 #### Buffer 的基本使用流程 使用 Buffer 的典型流程包括以下步骤: 1. **分配缓冲区**:使用静态方法 `allocate()` 创建一个指定大小的 Buffer。 2. **写入数据**:通过 `put()` 方法将数据写入 Buffer。 3. **翻转缓冲区**:调用 `flip()` 方法Buffer 从写模式切换到读模式。 4. **读取数据**:通过 `get()` 方法Buffer 中读取数据。 5. **清空缓冲区**:调用 `clear()` 或 `compact()` 方法准备下一次写入。 以下是一个简单的 `ByteBuffer` 使用示例: ```java import java.nio.ByteBuffer; public class BufferExample { public static void main(String[] args) { // 分配一个容量为10的ByteBuffer ByteBuffer buffer = ByteBuffer.allocate(10); // 写入数据 buffer.put((byte) 'H'); buffer.put((byte) 'e'); buffer.put((byte) 'l'); buffer.put((byte) 'l'); buffer.put((byte) 'o'); // 翻转缓冲区,准备读取 buffer.flip(); // 读取数据 while (buffer.hasRemaining()) { System.out.print((char) buffer.get()); } // 清空缓冲区 buffer.clear(); } } ``` #### Buffer 的关键方法详解 - **`put()` 方法**:用于将数据写入 Buffer。`put()` 有多个重载版本,支持写入单个字节、字节数组以及指定位置写入 [^1]。 - **`flip()` 方法**:将 Buffer 从写模式切换到读模式。它会将 `limit` 设置为当前的 `position`,并将 `position` 重置为 0,这样就可以从头开始读取数据 [^1]。 - **`clear()` 方法**:将 Buffer 重置为写模式。它会将 `position` 设为 0,`limit` 设为 `capacity`,但不会清除 Buffer 中的数据 [^5]。 - **`compact()` 方法**:类似于 `clear()`,但不会清空整个 Buffer,而是保留未读取的数据,适合连续读写操作。 - **`mark()` `reset()` 方法**:用于标记当前位置并在之后恢复到该位置。 #### Buffer 的应用场景 - **文件拷贝**:使用 `FileChannel` `ByteBuffer` 可以高效地进行文件拷贝操作。 - **网络通信**:在网络编程中,Buffer 用于接收发送数据包。 - **内存映射文件**:通过 `MappedByteBuffer` 可以将文件直接映射到内存中,提升 I/O 性能。 #### 常见问题与解决方案 1. **Buffer 未翻转导致读取失败** 如果在写入数据后没有调用 `flip()`,则读取时 `position` 仍在末尾,无法读取到数据。解决方案是在写入完成后调用 `flip()`。 2. **Buffer 未清空导致数据残留** 如果多次写入数据而没有调用 `clear()` 或 `compact()`,可能会读取到旧数据。解决方案是在每次写入前调用 `clear()` 或根据需求使用 `compact()`。 3. **Buffer 容量不足** 如果写入的数据超过 Buffer 的容量,会导致 `BufferOverflowException`。解决方案是提前分配足够大的 Buffer 或使用动态扩容机制。 4. **多线程环境下 Buffer 的线程安全问题** Buffer 本身不是线程安全的,多个线程同时操作可能导致状态混乱。解决方案是使用线程局部变量(ThreadLocal)或同步机制。 #### 总结 Java NIO 中的 Buffer 是高效 I/O 操作的关键组件。通过合理使用 Buffer方法状态管理,可以显著提升程序的性能稳定性。掌握 Buffer 的生命周期、状态变化以及常见问题的解决方法,是开发高性能 I/O 程序的基础。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值