Wrayの知识库 Wrayの知识库
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
  • Java章节编写规范
  • Java基础

  • Java集合

  • Java并发

  • Java IO

    • Java IO概述
    • IO基础

    • 常用IO流

    • 文件与目录操作

    • Java NIO

      • NIO概述
      • Buffer缓冲区
        • 1. Buffer 的本质
        • 2. 三个核心属性
        • 3. 写模式和读模式
        • 4. clear 和 compact
        • 5. 典型读文件流程
        • 6. 堆内内存和直接内存
        • 7. 常见错误
        • 专家实践清单
        • Tips 快问快答
        • 8. 总结
      • Channel通道
      • Selector多路复用
      • SocketChannel网络编程
      • NIO与IO对比
    • 高级主题

    • 实践与性能优化

    • 面试与设计题

    • IO实践速查

  • JVM

目录

Buffer缓冲区

Buffer 是 NIO 的核心。传统 IO 中数据从流里来,NIO 中数据必须先进入缓冲区,程序再从缓冲区读取或写入。

# 1. Buffer 的本质

Buffer 本质上是一块内存区域,用于临时存放数据。

ByteBuffer buffer = ByteBuffer.allocate(1024);

常见 Buffer:

  • ByteBuffer
  • CharBuffer
  • IntBuffer
  • LongBuffer
  • DoubleBuffer
  • MappedByteBuffer

网络和文件 IO 中最常用的是 ByteBuffer。

# 2. 三个核心属性

Buffer 有三个非常重要的属性:

  • capacity:容量,缓冲区最大能存多少数据。
  • position:当前位置,下一次读或写的位置。
  • limit:边界,读写不能超过的位置。

还有一个可选属性:

  • mark:标记位置,可通过 reset() 回到标记点。

# 3. 写模式和读模式

Buffer 最容易让初学者困惑的是模式切换。

写入数据:

ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put((byte) 1);
buffer.put((byte) 2);

此时 position 向后移动,表示写到了哪里。

读取前必须调用 flip():

buffer.flip();
while (buffer.hasRemaining()) {
    byte b = buffer.get();
}

flip() 的作用是:

  • 把 limit 设置为当前 position。
  • 把 position 设置为 0。

这样 Buffer 就从写模式切换到读模式。

# 4. clear 和 compact

读取完成后,如果要继续写入,可以调用 clear():

buffer.clear();

clear() 不会真正清空内存数据,只是重置指针:

  • position = 0
  • limit = capacity

如果还有部分数据没读完,但希望保留未读数据并继续写入,可以使用 compact():

buffer.compact();

compact() 会把未读数据移动到缓冲区开头,然后切换到写模式。

# 5. 典型读文件流程

try (FileChannel channel = FileChannel.open(Path.of("data.txt"), StandardOpenOption.READ)) {
    ByteBuffer buffer = ByteBuffer.allocate(1024);

    while (channel.read(buffer) != -1) {
        buffer.flip();

        while (buffer.hasRemaining()) {
            byte b = buffer.get();
            // process byte
        }

        buffer.clear();
    }
}

这个流程是 NIO Buffer 的基本套路:

  1. Channel 读取数据到 Buffer。
  2. flip() 切换为读模式。
  3. 程序读取 Buffer。
  4. clear() 或 compact() 准备下一轮写入。

# 6. 堆内内存和直接内存

ByteBuffer 有两种常见分配方式:

ByteBuffer heapBuffer = ByteBuffer.allocate(1024);
ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024);

allocate 创建堆内缓冲区,内存由 JVM 堆管理。

allocateDirect 创建直接缓冲区,内存在堆外,和操作系统 IO 交互时可能减少一次复制。

直接缓冲区适合:

  • 大文件读写。
  • 高频网络 IO。
  • 生命周期较长、复用率高的缓冲区。

不适合:

  • 大量短生命周期小 Buffer。
  • 没有明显 IO 性能瓶颈的普通代码。

# 7. 常见错误

  • 写完后忘记 flip(),导致读不到数据。
  • 读完后忘记 clear(),导致后续无法继续写入。
  • 不理解 clear() 不会清除真实数据。
  • 多线程共享同一个 Buffer,没有同步控制。
  • 盲目使用直接内存,导致堆外内存压力和排查困难。

# 专家实践清单

操作 作用 常见错误
put 写入 Buffer 写满后未检查剩余空间
flip 写模式切到读模式 忘记调用导致读不到数据
get 从 Buffer 读取 读取超过 limit
clear 切回写模式 误以为清除了真实数据
compact 保留未读数据再写 粘包半包处理不当

Buffer 状态机:

写模式:position 前进,limit=capacity
  │ flip
  ▼
读模式:position=0,limit=原写入位置
  │ clear / compact
  ▼
写模式:继续接收数据

# Tips 快问快答

Q:Buffer 的三个核心指针是什么? A:capacity、position、limit。

Q:flip 做了什么? A:把 limit 设为当前 position,再把 position 置 0,准备读取。

Q:clear 会清空底层数组吗? A:不会,只重置 position 和 limit。

Q:compact 适合什么场景? A:读了一部分后保留未读数据,再继续写入新数据。

Q:直接缓冲区和堆缓冲区区别是什么? A:直接缓冲区在堆外,适合底层 IO,但分配和回收成本更高。

Q:Buffer 线程安全吗? A:不是,多线程共享需要额外同步。

Q:为什么网络编程容易用到 compact? A:TCP 可能半包,未解析完的数据需要保留到下一次读取。

Q:Buffer 最常见的新手错误是什么? A:忘记在写入后调用 flip 再读取。

# 8. 总结

Buffer 是 NIO 的数据容器。掌握 Buffer 的关键是理解 capacity、position、limit,以及 flip()、clear()、compact() 这三个方法。只要这套状态机清楚了,NIO 文件和网络读写就会顺很多。

上次更新: 2026/06/25, 14:19:18
NIO概述
Channel通道

← NIO概述 Channel通道→

Copyright © 2023-2026 Wray | 鄂ICP备2024050235号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式