- ByteBuffer类位于java.nio包下,所谓nio:代表new io,另一种解释:N代表Non-blocking IO,非阻塞的IO
Buffer是一个抽象的基类
派生类:ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer
Buffer的几个基本属性
- 基础属性,使用ByteBuffer是以字节Byte为基础的,操作对象是字节
- capacity--Buffer的容量,读取数据的限制和读取数据的位置,capacity 是分配好的一个内存块大小,分配好后大小不可变
- limit--在读的模式下,表示缓存内数据的多少,limit<=capacity; 在写的模式下,表示最多能存入多少数据,此时limit=capacity;简单说:在Buffer上进行的读写操作都不能越过这个下标。
- position--表示读写的位置,下标从0开始
Buffer的几个方法
- clear()--position置为0,limit转为capacity大小,标记mark也会被清除
- flip()--limit被置为当前position的大小,也就是说后续的读操作可能会被限制,这是与clear()有区别的地方;然后position被置为0
- rewind()--position置为0,标记被取消
使用举例
基本用法
ByteBuffer byteBuffer = ByteBuffer.allocate(10);//分配10个字节大小的缓存 byteBuffer.putInt(5);//int占用4个字节 byteBuffer.put((byte) 1);//1个字节 byteBuffer.put((byte) 0);//1个字节 byteBuffer.put("abc".getBytes());//2个字节 System.out.printf("position: %d, limit: %d, capacity: %d\n", buf.position(), buf.limit(), buf.capacity());
打印结果:
position: 9, limit: 10, capacity: 10
分析:byteBuffer刚开始分配了10个字节的缓存,放入一个Int值5,占用4个字节,两个byte数又占两个,adb占用三个字节,总共占用了9个字节。从打印结果中看到,position为9表示已经存储了9个字节即下标0-8,limit表示在写模式下最多存储10个字节,capacity为10表示缓存容量为10。
java.nio.BufferOverflowException
因为ByteBuffer的大小不可变的,所以如果存储的长度超过capacity,那么就会抛出java.nio.BufferOverflowException异常,表示缓冲区上溢出,写入的位置超出了内存长度。
flip()和clear()
flip方法的作用是先将limit属性设置为当前的position的值,再将position转为0,并清除标记。
clear方法的不同之处是limit=capacity。
ByteBuffer byteBuffer = ByteBuffer.allocate(10); byteBuffer.putInt(5);//int占用4个字节 byteBuffer.put((byte) 1); byteBuffer.put((byte) 0); byteBuffer.put("abcd".getBytes()); printByteBuffer(byteBuffer); // 读取消息头,因为写完后position已经到10了,所以需要先反转为0,再从头读取 byteBuffer.flip();//将position置0 //需要按顺序读取,position自增 System.out.printf("读取int值: %d\n", byteBuffer.getInt());//读int System.out.printf("读取byte值: %d\n", byteBuffer.get());//读byte System.out.printf("读取byte值: %d\n", byteBuffer.get());//读byte byte[] str = new byte[4]; byteBuffer.get(str, 0, 4);//连续读4个字节,并保存到str中,注意第二个参数是相对于position的偏移量 System.out.println("读取字符串=" + new String(str));
打印结果如下:
position: 10, limit: 10, capacity: 10 读取int值: 5 读取byte值: 1 读取byte值: 0 读取字符串=abcd
java.nio.BufferUnderflowException
试一下flip()的使用。上面介绍中提到,flip()的作用是先将limit属性设置为当前的position的值,再将position转为0。也就是说如果当前的position是小于实际保存的字节长度的,那么下次就最多读取到这个position的位置,如果超出则会抛出异常。
ByteBuffer byteBuffer = ByteBuffer.allocate(10); byteBuffer.putInt(5);//int占用4个字节 byteBuffer.put((byte) 1); byteBuffer.put((byte) 0); byteBuffer.put("abcd".getBytes()); byteBuffer.clear(); //先读取一个int,再flip() System.out.printf("读取int值: %d\n", byteBuffer.getInt());//读int byteBuffer.flip(); System.out.printf("position: %d, limit: %d, capacity: %d\n", byteBuffer.position(), byteBuffer.limit(), byteBuffer.capacity()); //需要按顺序读取,position自增 System.out.printf("读取int值: %d\n", byteBuffer.getInt());//读int System.out.printf("读取byte值: %d\n", byteBuffer.get());//读byte System.out.printf("读取byte值: %d\n", byteBuffer.get());//读byte
打印结果:
读取int值: 5 position: 0, limit: 4, capacity: 10 读取int值: 5 java.nio.BufferUnderflowException at java.nio.Buffer.nextGetIndex(Buffer.java:500) at java.nio.HeapByteBuffer.get(HeapByteBuffer.java:135) at com.heima.ajax.JacksonTest.testByteBuffer(JacksonTest.java:83)
此时报出了内存下溢出的异常,说明读取的位置超出了限制。