java笔记--NIO基础--Buffer缓冲区

缓冲区 Buffer

// Invariants: mark <= position <= limit <= capacity       Buffer内部维护四个成员变量保证运行
private int mark = -1;		//起始标记,操作position位置
private int position = 0;		//记录当前位置,控制读取操作,类似于迭代器
private int limit;		//限制position最大位置,会改变
private int capacity;		//缓冲区的容量,不会变
实现子类
子类描述
ByteBuffer存储字节(byte),最常用的 Buffer 类型,内定义了数组
CharBuffer存储字符(char)
ShortBuffer存储短整型(short)
IntBuffer存储整型(int)
LongBuffer存储长整型(long)
FloatBuffer存储浮点型(float)
DoubleBuffer存储双精度浮点型(double)
真正实现类
Buffer 抽象子类常见实现类描述
ByteBufferHeapByteBufferDirectByteBuffer最常用的 Buffer 类型,有堆内和直接内存实现,数组存在堆中,使用父类数组,实现父类定义的操作
CharBufferHeapCharBufferDirectCharBufferU存储 char 类型数据
ShortBufferHeapShortBufferDirectShortBufferU存储 short 类型数据
IntBufferHeapIntBufferDirectIntBufferU存储 int 类型数据
LongBufferHeapLongBufferDirectLongBufferU存储 long 类型数据
FloatBufferHeapFloatBufferDirectFloatBufferU存储 float 类型数据
DoubleBufferHeapDoubleBufferDirectDoubleBufferU存储 double 类型数据
如何创建缓冲区
public static void main(String[] args) throws IOException {
    // 第一种创建缓冲区的方法
    IntBuffer buffer = IntBuffer.allocate(10); //底层返回的是 HeapIntBuffer 实例,即在java堆上分配一个字节缓冲区
    // 第二种创建缓冲区的方法
    int[] array = {1, 2, 3, 4, 5};
    IntBuffer buffer1 = IntBuffer.wrap(array);
    // 第三种创建缓冲区的方法
    IntBuffer buffer2 = IntBuffer.wrap(array, 1, 3);
}
缓冲区写操作
public static void main(String[] args) {
    IntBuffer buffer = IntBuffer.allocate(10);
    buffer.put(1); //position = 0,写入 1,position 变为 1 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    buffer.put(1,999); //不影响 position,索引 1 被改为 999 [1, 0, 999, 0, 0, 0, 0, 0, 0, 0]
    int[] arr = new int[]{666,555};
    buffer.put(arr); //从当前 position(1)开始,写入 666、555  [1, 666, 555, 0, 0, 0, 0, 0, 0, 0]
    buffer.put(0, arr); //从索引 0 开始,写入 666、555  [666, 555, 555, 0, 0, 0, 0, 0, 0, 0]
    buffer.put(arr, 1, 1); //从数组索引 1 开始,写入 555 [666, 555, 555, 555, 0, 0, 0, 0, 0, 0]
    buffer.put(0, arr, 1, 1); //从索引 0 开始,从数组索引 1 开始,写入 555 [555, 555, 555, 555, 0, 0, 0, 0, 0, 0]
    IntBuffer buffer1 = IntBuffer.wrap(new int[]{1,2,3,4,5,6,7,8,9,10});
    buffer.put(4,buffer1,0,6); // 从索引 4 开始,写入 buffer1 的 0-6 索引 [555, 555, 555, 555, 1, 2, 3, 4, 5, 6]
    buffer.put(buffer1); // 从当前 position(4)开始,写入 buffer1,注意此时会溢出
    System.out.println(Arrays.toString(buffer.array()));
}
缓冲区读操作

注意,执行完写操作后想要进行读之前,要执行filp()方法翻转:limit = position; position = 0;

方法是否预设数据初始 position初始 limit读前是否要 flip
wrap(arr)✅ 是0arr.length❌ 不需要
allocate(capacity)❌ 否0capacity✅ 需要

image-20250504103103020

public static void main(String[] args) { //参数有index的不影响position位置
    int[] arr = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    IntBuffer buffer = IntBuffer.wrap(arr);
    buffer.get(); // 读 position=0,返回 1,position++
    buffer.get(); // 读 position=1,返回 2,position++
    buffer.get(1); // 直接访问索引 1(值为 2),position 不变
    int[] arr2 = new int[8];
    //buffer.get(arr2); // 从当前 position=2 开始,连续读 8 个元素(读满arr2) 到 arr2,position=10
    //buffer.get(arr2, 3, 3); // 从当前 position=2 ,连续读 3 个元素 到 arr2[3]及后面 3 个位置,position=5
    //buffer.get(0,arr2); // 从索引 0 开始,连续读 8 个元素(读满arr2) 到 arr2,position不变
    buffer.get(0,arr2, 3, 3); // 从索引 0 开始,连续读 3 个元素 到 arr2[3]及后面 3 个位置,position不变
    System.out.println(buffer);
    System.out.println(Arrays.toString(arr2));
    System.out.println(Arrays.toString(buffer.array()));//获取的是底层数组,即改变底层数组后buffer也会变
}
public static void main(String[] args) { //mark和reset方法进行读操作
    int[] arr = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    IntBuffer buffer = IntBuffer.wrap(arr);
    buffer.get(); // 读 position=0,返回 1,position++
    buffer.mark(); // 标记 position=1
    System.out.println(buffer.get()); // 读 position=1,返回 2,position++
    buffer.reset(); // 重置 position=1,该方法不能单独出现,否则会报错
    System.out.println(buffer.get()); // 读 position=1,返回 2,position++
}
其他缓冲区操作
方法作用常见用途是否影响数据
flip()写转读写完准备读取❌ 不改内容
clear()清空缓冲区,准备写读完准备写新数据❌(内容还在,只是重置)
rewind()重绕到头想重复读取数据
compact()压缩未读数据到前面,准备继续写边读边写、半包处理等✅ 改位置
mark() + reset()标记/回到某个位置回退、回看
hasRemaining() / remaining()判断/获取剩余数据循环中判断读完没
duplicate()拷贝一个共享数据的新 Buffer(独立指针)多线程或不同模块使用同样数据
slice()从当前 position 到 limit 的视图子缓冲区处理部分数据❌(子视图)
缓冲区比较
方法名作用是否比较内容说明
equals(Buffer other)判断两个 Buffer 内容是否相等只比较 剩余内容,不比容量等
compareTo(Buffer other)按字典序比较两个 Buffer 的内容也只比较 剩余内容,遵循 Comparable 接口
==比较引用是否相同比较的是对象地址
只读缓冲区 ReadOnlyBuffer
public static void main(String[] args) {
    int[] arr = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    IntBuffer buffer = IntBuffer.wrap(arr);
    IntBuffer readOnlyBuffer = buffer.asReadOnlyBuffer();//只读缓冲区,返回的是HeapIntBufferR实例对象,继承自HeapIntBuffer
    while (readOnlyBuffer.hasRemaining()){
        System.out.println(readOnlyBuffer.get());
    }
    arr[0] = 100;//只读缓冲区不能修改数据,但是可以通过修改底层数组或buffer来修改数据
    readOnlyBuffer.rewind();
    while (readOnlyBuffer.hasRemaining()){
        System.out.println(readOnlyBuffer.get());//只读缓冲区和buffer共享底层数组,相当于原buffer的镜像,数据共享但禁止写入
    }
}
BypeBufferCharBuffer
项目ByteBufferCharBuffer
存储单位byte(1字节)char(2字节,UTF-16)
常见用途网络/文件读写、序列化、通用原始数据处理字符串、字符编码
数据长度单位字节字符(2字节)
编码处理常配合 CharsetDecoder/CharsetEncoder可直接处理 Unicode 字符
可否转换可用 .asCharBuffer() 创建视图可通过编码转成 ByteBuffer
ByteBuffer buf = ByteBuffer.allocate(10);
buf.put((byte) 65); // 写入字节值 65(ASCII 的 'A')
buf.flip();
System.out.println(buf.get()); // 输出 65
CharBuffer cbuf = CharBuffer.allocate(10);
cbuf.put('你');
cbuf.put('好');
cbuf.flip();
System.out.println(cbuf.get()); // 输出:你
Charset charset = StandardCharsets.UTF_8;//使用字符编码解码
ByteBuffer byteBuf = charset.encode("你好");
CharBuffer charBuf = charset.decode(byteBuf);
System.out.println(charBuf.toString()); // 输出:你好
直接缓冲区
对比项直接缓冲区 (allocateDirect)普通缓冲区 (allocate)
内存位置堆外(OS内存)JVM 堆内
是否被 GC 管理❌ 不受 GC 控制✅ 会被 GC 管理
IO 性能快(省一次复制)慢(数据需从堆拷贝到内核)
分配/释放成本高(申请释放慢)
是否可访问底层数组hasArray() 返回 false✅ 可用 array() 方法
普通缓冲区:
[Channel] ←→ [内核缓冲区] ←→ [JVM堆缓冲区] ←→ [你的代码]

直接缓冲区:
[Channel] ←→ [内核缓冲区] ←→ [堆外缓冲区] ←→ [你的代码]

场景是否推荐使用直接缓冲区
文件读取/写入✅ 是
网络 socket 数据传输✅ 是
临时变量、小数据❌ 不推荐(分配成本高)
多次重复使用大数据块✅ 非常适合
                  ┌────────────────────────────┐
                  │  ByteBuffer.allocateDirect()                       │
                  └────────────┬───────────────┘
                                                 ↓
                                        [ JVM 调用 native 方法 ]
                                                 ↓
                                      [ 分配堆外内存(DirectMemory) ]
                                                 ↓
                                        [ 创建 DirectByteBuffer 对象 ]
                                                 ↓
                               ┌──── 使用 ────→ put()/get()/Channel IO ────┐
                               ↓                                             								    ↓
                        [ 只释放 Java 引用 ]                            				        [ 等待 GC 回收 ]
                                   ↓                                             								↓
                            [ Cleaner 自动触发 native free 方法释放内存 ]
阶段过程关键点
加载allocateDirect()native 分配堆外内存
使用put/get/channel 读写不走 JVM 堆,效率高
释放依赖 GC 和 Cleaner不可手动释放,不能频繁创建
         |

| ---- | -------------------- | -------------------------- |
| 加载 | allocateDirect() | native 分配堆外内存 |
| 使用 | put/get/channel 读写 | 不走 JVM 堆,效率高 |
| 释放 | 依赖 GC 和 Cleaner | 不可手动释放,不能频繁创建 |

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值