ByteBuffer 是jdk内部提供的字节缓冲区,内存分配主要分为堆内存和直接内存(堆外内存)。

NIO-ByteBuffer_bc

1、堆内存分配


//分配一个容量为32的堆缓冲区 ByteBuffer buffer = ByteBuffer.allocate(32);


2、直接内存


ByteBuffer buffer = ByteBuffer.allocateDirect(32);


 

字节数据操作

ByteBuffer 有4个属性 capacity、limit、position、mark

capacity:容量,即可以容纳的最大数据量;在缓冲区创建时被设定并且不能改变。

limit:表示缓冲区的当前终点,不能对缓冲区超过极限的位置进行读写操作。可以通过下面的方法修改


public final Buffer limit(int newLimit)


position: 位置,下一个要被读或写的元素的索引,每次读写缓冲区数据时都会改变改值,为下次读写作准备。可以通过下面的方法修改


public final Buffer position(int newPosition)


mark标记: 调用mark()来设置mark=position,再调用reset()可以让position恢复到标记的位置

示例代码:

@Test
    public void test(){
        //分配一个容量为32的堆缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(32);
        //初始状态
        System.out.println(String.format("初始状态 limit:%d,capacity:%d,position:%d",buffer.limit(),buffer.capacity(),buffer.position()));
        //添加字节
        buffer.put("hello".getBytes());
        //输出
        System.out.println(String.format("添加字节后 limit:%d,capacity:%d,position:%d",buffer.limit(),buffer.capacity(),buffer.position()));
        //读取的内容是position到limit之间数据 调用该方法设置 limit = position;position = 0;mark = -1;
        buffer.flip();
        //输出
        System.out.println(String.format("flip后--》 limit:%d,capacity:%d,position:%d",buffer.limit(),buffer.capacity(),buffer.position()));
        //读取数据
        byte[] newArray = new byte[buffer.remaining()] ;
        buffer.get(newArray);
        String value = new String(newArray);
        System.out.println("读取到的数据:"+value);
        System.out.println(String.format("读取后--》 limit:%d,capacity:%d,position:%d",buffer.limit(),buffer.capacity(),buffer.position()));

        //调用mark标记当前position的值
        buffer.mark();
        //设置limit的值 position到limit的长度为可写的长度 所有设置为buffer.position()+新字节的长度
        buffer.limit(buffer.position()+"abcdef".getBytes().length);
        buffer.put("abcdef".getBytes());
        //写入后position会向后移动 position=limit
        System.out.println(String.format("再次写入后--》 limit:%d,capacity:%d,position:%d",buffer.limit(),buffer.capacity(),buffer.position()));

        //读取新写入的内容,需要将position设置为mark的位置
        buffer.reset();
        System.out.println(String.format("再次读取reset后--》 limit:%d,capacity:%d,position:%d",buffer.limit(),buffer.capacity(),buffer.position()));
        //读取数据
        byte[] newArray2 = new byte[buffer.remaining()] ;
        buffer.get(newArray2);
        String value2 = new String(newArray2);
        System.out.println("读取到的数据:"+value2);
        System.out.println(String.format("读取后--》 limit:%d,capacity:%d,position:%d",buffer.limit(),buffer.capacity(),buffer.position()));
    }

运行结果:

NIO-ByteBuffer_System_02

 

分析:

1、向一个容量为32的字节缓冲区中写入hello字符后,position=5

NIO-ByteBuffer_System_03

2、读取写入的字节必须调用flip,调用后limit = position=5;position = 0;mark = -1; 

NIO-ByteBuffer_System_04

然后调用下面的方法去读取缓冲区的数据。buffer.remaining()=limit - position,读取的内容是从position到limit之间的内容。


byte[] newArray = new byte[buffer.remaining()] ; buffer.get(newArray);


NIO-ByteBuffer_ci_05

 

3、读取后,position指针会向后移动,position=limit

NIO-ByteBuffer_bc_06

4、再次写入字节abcdef,需要调用mark方法,记录一下当前position的值

NIO-ByteBuffer_ci_07

设置limit的值为(position+abcdef的长度),然后调用put方法写入字节。

NIO-ByteBuffer_System_08

调用buffer.reset(); 将position设置为mark的值(5)

NIO-ByteBuffer_System_09

NIO-ByteBuffer_System_10

最后读取position与limit之间的字节数据就是abcdef。

 

Buffer方法汇总:

reset():    把position设置成mark的值,相当于之前做过一个标记,现在要退回到之前标记的地方
 clear():    position = 0;limit = capacity;mark = -1;  有点初始化的味道,但是并不影响底层byte数组的内容
 flip():    limit = position;position = 0;mark = -1;  翻转,也就是让flip之后的position到limit这块区域变成之前的0到position这块,翻转就是将一个处于存数据状态的缓冲区变为一个处于准备取数据的状态
 rewind():    把position设为0,mark设为-1,不改变limit的值
 remaining():    return limit - position;返回limit和position之间相对位置差
 hasRemaining():    return position < limit返回是否还有未读内容
 compact():    把从position到limit中的内容移到0到limit-position的区域内,position和limit的取值也分别变成limit-position、capacity。如果先将positon设置到limit,再compact,那么相当于clear()
 get():    相对读,从position位置读取一个byte,并将position+1,为下次读写作准备
 get(int index):    绝对读,读取byteBuffer底层的bytes中下标为index的byte,不改变position
 get(byte[] dst, int offset, int length):    从position位置开始相对读,读length个byte,并写入dst下标从offset到offset+length的区域
 put(byte b):    相对写,向position的位置写入一个byte,并将postion+1,为下次读写作准备
 put(int index, byte b):    绝对写,向byteBuffer底层的bytes中下标为index的位置插入byte b,不改变position
 put(ByteBuffer src):    用相对写,把src中可读的部分(也就是position到limit)写入此byteBuffer
 put(byte[] src, int offset, int length):    从src数组中的offset到offset+length区域读取数据并使用相对写写入此byteBuffer