想写下ByteBuffer的原因是最近在看一个开源的框架,里面用到了Netty的ByteBuf,我之前用netty做开发的时候用的不深,也没有用到Netty自带的ByteBuf。所以想好好写写ByteBuf让自己的netty水平更上一层楼。要想写ByteBuf就得想复习复习JDK中的ByteBuffer,所以,本文先复习下ByteBuffer。

  ByteBuffer有两种分配,一种是堆内分配一种是堆外分配,为了方便描述本文只考虑堆内分配。

一 ByteBuffer介绍

  ByteBuffer.wrap(byte[] data)

  ByteBuffer buffer = ByteBuffer.allocate(10);

  ByteBuffer的堆内分配有两种方式,就是上面示例的两种。

  

ByteBuffer包含几个基本的属性:

  • position:当前的下标位置,表示进行下一个读写操作时的起始位置;
  • limit:结束标记下标,表示进行下一个读写操作时的(最大)结束位置;换句话说,一个容量为10的字节数组,它的limit初始是10,因为0-9都是可写的。limit表示可读或者可写的下一个位置
  • capacity:该ByteBuffer容量;
  • mark: 自定义的标记位置;

  这里要记住有四个属性,不过mark这个属性相对用的少,至少我没怎么用到过。其他三种属性要牢记。

  position,limit,capacity

二 常用API示例

public static void main(String[] args) {

        //分配10字节大小内存空间
        ByteBuffer buffer = ByteBuffer.allocate(10);
        //输出初始化后position的值
        System.out.println("初始化position : " + buffer.position());// 初始化position : 0
        //输出初始化收limit的值
        System.out.println("初始化limit : " + buffer.limit()); // 初始化limit : 10
        //输出初始化后capacity的值
        System.out.println("初始化capacity : " + buffer.capacity()); // 初始化capacity : 10
        //输出初始化后ByteBuffer内容
        printBuffer(buffer); //0000000000

        //调用rewind()之前指针指向下标9即位置10,已经是最大容量
        //调用rewind()之后将指针移动到下标0即位置1
        buffer.rewind();
        System.out.println("position:" + buffer.position() + ",limit:" + buffer.limit() + ",capacity:" + buffer.capacity()); position:0,limit:10,capacity:10
        //执行写入操作,指针会自动移动
        buffer.putChar('a');
        //输出指针position,指针指向下标2即位置3
        System.out.println("position:" + buffer.position() + ",limit:" + buffer.limit() + ",capacity:" + buffer.capacity()); position:2,limit:10,capacity:10
        buffer.putChar('啊');
        //输出指针position,指针指向下标4即位置5
        System.out.println("position:" + buffer.position() + ",limit:" + buffer.limit() + ",capacity:" + buffer.capacity()); position:4,limit:10,capacity:10

//        buffer.rewind();
//        System.out.println("position:" + buffer.position() + ",limit:" + buffer.limit() + ",capacity:" + buffer.capacity());
        
        //将当前位置设置为EOF,指针移动到下标0即位置1
        buffer.flip();
        System.out.println("position:" + buffer.position() + ",limit:" + buffer.limit() + ",capacity:" + buffer.capacity()); position:0,limit:4,capacity:10
        //上一行代码相当于下面两句
        //buffer.limit(4);
        //buffer.position(0);

        //输出ByteBuffer内容,即0 61 55 4a
        printBuffer(buffer);
        //将指针移动到下标1即位置2
        buffer.position(1);
        //进行compact压缩操作,compact操作会将EOF位置重置为最大容量10
        //注意:该压缩操作是将下标1即位置2到位置4的值移动到位置1到位置3,位置4上的值4a不变
        buffer.compact();
        System.out.println("position:" + buffer.position() + ",limit:" + buffer.limit() + ",capacity:" + buffer.capacity()); position:3,limit:10,capacity:10
        //输出ByteBuffer内容,即61 55 4a 4a 0 0 0 0 0 0(注意:未覆盖到的位置4的值4a不变)
        printBuffer(buffer);

        //注意:执行压缩compact操作后指针指向下标3即位置4,继续写入数据时会覆盖数据
        System.out.println(buffer.position());
    }

 

public static void printBuffer(ByteBuffer buffer){

        //记录当前位置
        int position = buffer.position();
        //指针移动到0
        buffer.position(0);
        //循环输出每个字节内容
        for(int i = 0;i < buffer.limit();i++){
            //读取操作,指针会自动移动
            byte b = buffer.get();
            System.out.print(Integer.toHexString(b));
        }
        //指针再移动到标记位置
        buffer.position(position);
        System.out.println();
    }

结果为

初始化position : 0
初始化limit : 10
初始化capacity : 10
0000000000
position:0,limit:10,capacity:10
position:2,limit:10,capacity:10
position:4,limit:10,capacity:10
position:0,limit:4,capacity:10
061554a
position:3,limit:10,capacity:10
61554a4a000000
3

三 重点api解释

  1  flip() 主要用来由写转换为读。

public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }

  2  rewind() 相比上面的flip,rewind不会设置limit

public final Buffer rewind() {
        position = 0;
        mark = -1;
        return this;
    }

  3 compact()

  compact的作用是把当前position到limit之间的所有字节移动到0号位置,同时position设为剩下的字节数大小。假设position到limit之间有3个字节,移动过后position为3。同时limit为capacity。

  compact通常用来在继续写数据前,做一次compact。这样position之前的数据都已经被读取过了,可以在新的position继续写。

四 总结

  flip表示从写入模式转换为读取模式,而compact是从读取模式再转换成写入模式。

  其实ByteBuffer还有大小端的问题,JAVA默认是大端序,而网络传输也是大端序。一般来说使用java进行文件读写不用考虑大小端的问题。如果碰到默认解决不了还是需要自行设置大小端序。