昨天,测试程序时,发现程序了java.nio.BufferOverflowException 异常,后来,在网上搜索了资料,终于解决了问题。这里记录一下。
ByteBuffer params = ByteBuffer.allocate(2);// 这里只分配了2个字节,下面的params.put(tmp);却put了3个字节的数据。所以导致 java.nio.BufferOverflowException 异常
params.order(ByteOrder.LITTLE_ENDIAN);
byte[] tmp = new byte[3];
tmp[0] = (byte) data1;
tmp[1] = (byte) data2;
tmp[2] = (byte) data3;
params.put(tmp);
错误原因:写入的长度超出了允许的长度:
如何解决这个问题呢?
添加写入长度与 ByteBuffer 中可写入的长度的判断,例如:
while (writeBuffer.remaining() > 0) {
writeBuffer.put((byte)0);
}
注意:你每次只写入一个字节,那就判断大于0就好了,如果不是一个记得修改条件哦!
总结
当 ByteBuffer.remaining() 小于要读取或写入的长度时,再执行读取或写入操作都会产生异常;
读取则产生 java.nio.BufferUnderflowException 异常,
写入则产生 java.nio.BufferOverflowException 异常。
当 ByteBuffer.remaining() 等于 0 时,不能再执行读取或写入操作,需要执行:clear() 操作,否则将产生异常。
这里介绍一下几个Buffer的函数
1、存取(Buffer.get() & Buffer.put())
使用get()从缓冲区中取数据,使用put()向缓冲区中存数据。
// 创建一个容量为10的byte数据缓冲区
ByteBuffer buff = ByteBuffer.allocate(10);
// 存入4次数据
buff.put((byte) 'A');
buff.put((byte) 'B');
buff.put((byte) 'C');
buff.put((byte) 'D');
// 翻转缓冲区
buff.flip();
// 读取2次数据
System.out.println((char)buff.get());
System.out.println((char)buff.get());
上面有提过缓冲区四个属性值一定遵循capacity>=limit>=position>=mark>=0,put()时,若position超过limit则会抛出BufferOverflowException;get()时,若position超过limit则会抛出BufferUnderflowException。
buff.flip()是将缓冲区翻转,翻转将在下面来说。
调用put()或get()时,每调用一次position的值会加1,指示下次存或取开始的位置;
再向Buffer中读写数据时有2个方法也非常有用:
Buffer.remaining():返回从当前位置到上界的数据元素数量;
Buffer.hasRemaining():告诉我们从当前位置到上界是否有数据元素;
2、翻转(Buffer.flip())
翻转就是将一个处于存数据状态的缓冲区变为一个处于准备取数据的状态,使用flip()方式实现翻转。Buffer.flip()的源码如下:
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
相信看到了实现的源码应该就会清楚flip()的作用了。rewind()方法与flip()很相似,区别在于rewind()不会影响limit,而flip()会重设limit属性值,Buffer.rewind()的源码如下:
public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}
3、压缩(Buffer.compact())
压缩就是将已读取了的数据丢弃,保留未读取的数据并将保留的数据重新填充到缓冲区的顶部,然后继续向缓冲区写入数据。
// 创建一个容量为10的byte数据缓冲区
ByteBuffer buff = ByteBuffer.allocate(10);
// 填充缓冲区
buff.put((byte)'A');
buff.put((byte)'B');
buff.put((byte)'C');
buff.put((byte)'D');
System.out.println("first put : " + new String(buff.array()));
//翻转
buff.flip();
//释放
System.out.println((char)buff.get());
System.out.println((char)buff.get());
//压缩
buff.compact();
System.out.println("compact after get : " + new String(buff.array()));
//继续填充
buff.put((byte)'E');
buff.put((byte)'F');
//输出所有
System.out.println("put after compact : " + new String(buff.array()));
以上代码打印结果:
first put : ABCD
A
B
compact after get : CDCD
put after compact : CDEF
控制台中输出内容中有正方形的乱码,是正常。因为字节缓冲区中没有赋值的内存块默认值是0,而Unicode编码中没有0编码,所以乱码。
这里简单这里一下:
方法 | 描述 |
---|---|
limit(), limit(10)等 | 其中读取和设置这4个属性的方法的命名和jQuery中的val(),val(10)类似,一个负责get,一个负责set |
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 |
以下为一些测试方法:如果需要存入一个byte数组,buffer.put(Byte[] b)和put(b, 0, b.length)是一样的。
android 解决java.nio.BufferOverflowException 异常就讲完了。
就这么简单。