想写下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进行文件读写不用考虑大小端的问题。如果碰到默认解决不了还是需要自行设置大小端序。