NIO编程中buffer对象的理解以及API的使用

时间:2024-08-07 09:03:56

概念讲解,转自https://www.cnblogs.com/lxzh/archive/2013/05/10/3071680.html  ,将的非常好!

Buffer 类是 java.nio 的构造基础。一个 Buffer 对象是固定数量的数据的容器,其作用是一个存储器,或者分段运输区,在这里,数据可被存储并在之后用于检索。缓冲区可以被写满或释放。对于每个非布尔原始数据类型都有一个缓冲区类,即 Buffer 的子类有:ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer 和 ShortBuffer,是没有 BooleanBuffer 之说的。尽管缓冲区作用于它们存储的原始数据类型,但缓冲区十分倾向于处理字节。非字节缓冲区可以在后台执行从字节或到字节的转换,这取决于缓冲区是如何创建的。 
◇ 缓冲区的四个属性 
        所有的缓冲区都具有四个属性来提供关于其所包含的数据元素的信息,这四个属性尽管简单,但其至关重要,需熟记于心:

  • 容量(Capacity):缓冲区能够容纳的数据元素的最大数量。这一容量在缓冲区创建时被设定,并且永远不能被改变。
  • 上界(Limit):缓冲区的第一个不能被读或写的元素。缓冲创建时,limit 的值等于 capacity 的值。假设 capacity = 1024,我们在程序中设置了 limit = 512,说明,Buffer 的容量为 1024,但是从 512 之后既不能读也不能写,因此可以理解成,Buffer 的实际可用大小为 512。
  • 位置(Position):下一个要被读或写的元素的索引。位置会自动由相应的 get() 和 put() 函数更新。 这里需要注意的是positon的位置是从0开始的。
  • 标记(Mark):一个备忘位置。标记在设定前是未定义的(undefined)。使用场景是,假设缓冲区中有 10 个元素,position 目前的位置为 2(也就是如果get的话是第三个元素),现在只想发送 6 - 10 之间的缓冲数据,此时我们可以 buffer.mark(buffer.position()),即把当前的 position 记入 mark 中,然后 buffer.postion(6),此时发送给 channel 的数据就是 6 - 10 的数据。发送完后,我们可以调用 buffer.reset() 使得 position = mark,因此这里的 mark 只是用于临时记录一下位置用的。

请切记,在使用 Buffer 时,我们实际操作的就是这四个属性的值。我们发现,Buffer 类并没有包括 get() 或 put() 函数。但是,每一个Buffer 的子类都有这两个函数,但它们所采用的参数类型,以及它们返回的数据类型,对每个子类来说都是唯一的,所以它们不能在顶层 Buffer 类中被抽象地声明。它们的定义必须被特定类型的子类所遵从。若不加特殊说明,我们在下面讨论的一些内容,都是以 ByteBuffer 为例,当然,它当然有 get() 和 put() 方法了。

API使用

这里再贴一些常用APi的练习code

//nio中Buffer的使用
public class BufferTest { @Test
public void test01(){
IntBuffer buf=IntBuffer.allocate(10);//创建指定长度的缓冲buffer
//pos: 存储或者读取到的数据的当前位置,下一个数据将从该position处后面的一个字节开始存储
//limit:缓冲区中数据的有效位置,默认和容量一样
//cap:初始容量
System.out.println(buf);//[pos=0 lim=10 cap=10]
buf.put(1); //pos 加一 put(int)方法 pos会加一
buf.put(2); //pos 加一
buf.put(3); //pos 加一
System.out.println(buf);//[pos=3 lim=10 cap=10]
buf.flip();//复位,pos变为o,limit变为buffer中实际数量
System.out.println(buf);//[pos=0 lim=3 cap=10]
buf.put(1,0); // 向某一个明确已有数据位置put数据,pos不变
buf.get(2); //获取某个下标数据时,pso也不变
//注意: put或get 某个下标时,必须小于limit-1(下标从0开始),否则会抛异常
System.out.println(buf);//[pos=0 lim=3 cap=10]
//循环获取时,需要用limit,不能用容量
for (int i = 0; i < buf.limit(); i++) {
//调用get方法会使其缓冲区位置(position)向后递增一位
System.out.print(buf.get() + "\t");
}
System.out.println();
System.out.println("buf对象遍历之后为: " + buf);//[pos=3 lim=3 cap=10]
} @Test
public void test02(){
/**
* wrap方法会包裹一个数组: 一般这种用法不会先初始化缓存对象的长度,因为没有意义,
* 最后还会被wrap所包裹的数组覆盖掉。并且wrap方法修改缓冲区对象的时候,数组本身也会跟着发生变化。
*/
IntBuffer buf = IntBuffer.allocate(10);
int[] arr=new int[]{1,2,5};
buf = IntBuffer.wrap(arr);
System.out.println(buf);//[pos=0 lim=3 cap=3]
//添加数组的前两个数进入buffer,容量还是原来数组的容量
IntBuffer buf01 = IntBuffer.wrap(arr, 0, 2);
System.out.println(buf01);//[pos=0 lim=2 cap=3] IntBuffer buf3 = buf.duplicate();//克隆一个buffer
System.out.println(buf3);
//buf.position(0);//设置buffer的pos.一般不建议使用这个,都使用后flap
//最佳遍历方式
buf.remaining();//获取目前可读数据长度,即limit长度
int[] arr2 = new int[buf.remaining()];
//将缓冲区数据放入arr2数组中去
buf.get(arr2);
for(int i : arr2){
System.out.print(Integer.toString(i) + "\t");//1 2 5
}
}

  如果有其他兴趣可以去上面转载地址深入研究或者自己深入研究!