《Java 100 天进阶之路》第23篇:缓冲区数据结构 ByteBuffer

第23篇:缓冲区数据结构 ByteBuffer


📌 系列导航《Java 100 天进阶之路》完整目录 |

⬅️ 上一篇:第22篇:Java字符串简介 |

➡️ 下一篇:第24篇:Java枚举类型 enum 用法


一、核心知识点

  • Buffer 抽象类与子类(ByteBufferCharBufferIntBuffer 等)
  • ByteBuffer 核心属性:capacitylimitpositionmark
  • 核心方法:put()get()flip()rewind()clear()compact()
  • 直接缓冲区 vs 堆缓冲区
  • NIO(New I/O)中的使用场景

二、通俗讲解(1分钟开心学)

1. 什么是 Buffer?

Buffer 是 Java NIO 中用于存储数据的容器,可以理解为一块可以读写的内存区域,并带有一套指针系统来管理读写位置。最常用的是 ByteBuffer

2. 四个核心属性

  • capacity:容量,创建后不可变。
  • limit:读写的极限位置。
  • position:下一个要读/写的位置索引。
  • mark:标记位置,可用 reset() 返回。

3. 两种模式

  • 写模式 :刚创建时,position = 0limit = capacity,往里面 put 数据。
  • 读模式 :调用 flip() 后,limit = position(原写到的位置),position = 0,然后 get 数据。

生活类比

缓冲区就像一个水池。capacity 是水池总容量。你在水池里倒水(写),position 是水面高度。倒完后,你标记一下现在的水位(flip),然后开始从底部取水(读)。取水时不能超过之前的水位(limit)。

4. 直接缓冲区 vs 堆缓冲区

  • 堆缓冲区allocate()):内存分配在 JVM 堆上,受 GC 管理,数据拷贝到 native 内存时多一次复制。
  • 直接缓冲区allocateDirect()):内存分配在系统本机内存(native heap),减少一次拷贝,适合大文件、网络传输,但分配和释放成本高。

三、实操代码案例 + 场景说明

场景 :使用 ByteBuffer 实现一个简单的"写数据 → 读数据"流程。

java 复制代码
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;

public class ByteBufferDemo {
    public static void main(String[] args) {
        // 1. 创建堆缓冲区,容量10
        ByteBuffer buffer = ByteBuffer.allocate(10);
        System.out.println("初始: pos=" + buffer.position() + ", limit=" + buffer.limit());
        
        // 2. 写入数据
        buffer.put((byte) 'H');
        buffer.put((byte) 'i');
        buffer.put((byte) '!');
        System.out.println("写入后: pos=" + buffer.position()); // 3
        
        // 3. 切换到读模式
        buffer.flip();
        System.out.println("flip后: pos=" + buffer.position() + ", limit=" + buffer.limit()); // pos=0, limit=3
        
        // 4. 读取数据
        while (buffer.hasRemaining()) {
            System.out.print((char) buffer.get());
        }
        System.out.println(); // 输出 "Hi!"
        
        // 5. rewind:重新读
        buffer.rewind();
        System.out.println("rewind后 pos=" + buffer.position()); // 0
        
        // 6. clear:清空状态,准备重新写
        buffer.clear();
        System.out.println("clear后 pos=" + buffer.position() + ", limit=" + buffer.limit()); // pos=0, limit=10
        
        // 7. 直接缓冲区示例
        ByteBuffer directBuf = ByteBuffer.allocateDirect(1024);
        if (directBuf.isDirect()) {
            System.out.println("这是直接缓冲区,适合网络传输");
        }
        
        // 8. 字符串与 ByteBuffer 互转
        String msg = "Hello NIO";
        ByteBuffer buf = ByteBuffer.wrap(msg.getBytes(StandardCharsets.UTF_8));
        // 读取时
        byte[] bytes = new byte[buf.remaining()];
        buf.get(bytes);
        String decoded = new String(bytes, StandardCharsets.UTF_8);
        System.out.println(decoded);
    }
}

四、避坑要点

错误/误区 后果 正确做法
写入后忘记 flip() 直接读取 读不到数据(position 在末尾) 写后读前必须 flip()
flip() 多次调用 limit 被错误设置 只在切换模式时调用一次
直接缓冲区频繁分配释放 性能反而下降 复用缓冲区或使用池化技术
使用 get() 前不判断 hasRemaining() 可能抛出 BufferUnderflowException 先判断或捕获异常

五、面试高频考点

Q1:ByteBufferflip() 方法做了什么?

limit 设为当前 positionposition 归零,为从写模式切换到读模式做准备。

Q2:直接缓冲区的优缺点?

优点:减少 JVM 堆和 native 堆之间的数据拷贝,提高 I/O 性能,适合大文件、网络。缺点:分配和释放成本高,不受 JVM 堆大小限制但受物理内存限制。

Q3:clear()compact() 的区别?

clear() 重置 position=0, limit=capacity,丢弃未读数据;compact() 将未读数据复制到缓冲区头部,position 移到未读数据后,limit=capacity,为写入留出空间。

六、练习题

  1. 代码填空:完成如下操作:写入字符串 "Java" 的字节,然后读取并打印。
  2. 简答:什么时候使用直接缓冲区?什么时候使用堆缓冲区?
  3. 动手 :用 ByteBufferFileChannel 实现文件拷贝(使用 transferToread/write)。

📊 你的学习进度

  • 当前:第23篇 / 共44篇 · 第三阶段:字符串、Buffer、枚举、类加载(第21~25篇)
  • ✅ 已完成:第1~22篇
  • 📖 正在学:第23篇
  • ⏳ 待学习:第24~44篇

👉 📚 完整目录 & 学习指南 | 🔥 订阅本专栏,不错过每一篇

💡 本专栏每篇都包含:避坑表 + 面试高频考点 + 练习题。每天30分钟,100天拿offer!


下一篇文章预告

《Java枚举类型 enum 用法》

内容简介:枚举的定义、构造器、成员变量和方法,values()/valueOf()/ordinal(),枚举单例模式。

💡 学完这篇,你将掌握枚举的全部用法,并用枚举写出更安全的代码。

📌 《Java 100 天进阶之路 | 从入门到上岗就业》 每天一篇,建议收藏 + 关注 ,一起100天拿offer!

👉 点击关注我,更新后第一时间收到推送!


相关推荐
还是鼠鼠1 小时前
AI掘金头条新闻系统 (Toutiao News)-获取新闻分类
后端·python·mysql·fastapi·web
REDcker1 小时前
C++循环与编译器优化详解 别名不变量向量化与GCC Clang验证及perf实践
java·jvm·c++·c·clang·gcc
lsx2024062 小时前
Razor VB 循环
开发语言
csuzhucong2 小时前
c++版本特性
开发语言·c++
高斯林.神犇2 小时前
Idea中使用Git
java·ide·intellij-idea
图码2 小时前
矩阵操作优化:从 O(q×n) 到 O(q) 的优雅进阶
数据结构·线性代数·算法·性能优化·矩阵·python3.11
超梦dasgg2 小时前
Spring Security 原理 + 生产环境认证授权实战
java·后端·spring
YL200404262 小时前
046二叉树展开为链表
数据结构·leetcode·链表
wand codemonkey2 小时前
【第五步+前后分离调】最后的联动调试--java+Vue3项目
java·开发语言·vue.js