Java NIO学习篇之缓冲区ByteBuffer详解
程序员文章站
2024-03-13 21:12:16
...
定义:
ByteBuffer是Buffer的实现类之一,是一个通用的缓冲区,功能要比其他缓冲区子类多。支持直接内存。是一个抽象类。子类实现是HeapByteBuffer(非直接缓冲区子类),DirectByteBuffer(直接缓冲区子类)。
看此文前建议看看:
关于Buffer可以查看 Java NIO学习篇之缓冲区Buffer详解
直接缓冲区:
以上是书《深入理解Java虚拟机》对直接内存的描述。简单来说直接内存不是JVM内存,而是计算机真正的物理内存。
我们IO中有个步骤是:
读:把内核缓冲区的数据复制到用户缓冲区。
写:把用户缓冲区的数据复制到内核缓冲区。
而使用直接缓冲区可以使得内核缓冲区和用户缓冲区映射到同一块物理内存地址上,使得可以省略复制步骤,实现零拷贝,提高效率。
这里它不是侧重点,简单了解下就好。
常用API详解(是对应子类HeapByteBuffer的效果):
获取ByteBuffer对象的API
//获取ByteBuffer对象的方法
//1. 获取非直接缓冲区的ByteBuffer,capacity参数指定缓冲区容量。
public static ByteBuffer allocate(int capacity);
//2. 通过传入一个字节数组来获取ByteBuffer对象。相当于把传进来的字节数组封装成一个ByteBuffer对象。
//如果传进来的数组有数据的话,可以直接进行读,此时ByteBuffer会处于读模式。创建的初始ByteBuffer的
//limit=该数组的长度length,position=0,所以读取的话会完全读取该数组的。
public static ByteBuffer wrap(byte[] array);
//3. 通过传入一个字节数组来获取ByteBuffer对象。相当于把传进来的字节数组封装成一个ByteBuffer对象。并且可以指定position(用参数offset指定),limit(用length+offset指定)。
public static ByteBuffer wrap(byte[] array,int offset, int length)
///////////////以上三种方法获取的都是非直接缓冲区/////////////
//4.获取直接缓冲区,并用capacity指定缓冲区的大小。
public static ByteBuffer allocateDirect(int capacity)
public class ByteBufferDemo {
public static void main(String[] args) {
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
System.out.println("============>>>allocate(int)<<<<==================");
System.out.println("position = " + byteBuffer.position());
System.out.println("limit = " + byteBuffer.limit());
System.out.println("capacity = " + byteBuffer.capacity());
byte[] b1 = new byte[1024];
ByteBuffer byteBuffer1 = ByteBuffer.wrap(b1);
System.out.println("============>>>wrap(byte[])<<<<==================");
System.out.println("position = " + byteBuffer1.position());
System.out.println("limit = " + byteBuffer1.limit());
System.out.println("capacity = " + byteBuffer1.capacity());
byte[] b2 = new byte[1024];
ByteBuffer byteBuffer2 = ByteBuffer.wrap(b1,10,500);
System.out.println("============>>>wrap(byte[],int,int)<<<<==================");
System.out.println("position = " + byteBuffer2.position());
System.out.println("limit = " + byteBuffer2.limit());
System.out.println("capacity = " + byteBuffer2.capacity());
System.out.println("============>>>allocateDirect(int)<<<<==================");
ByteBuffer byteBuffer3 = ByteBuffer.allocateDirect(1024);
System.out.println("position = " + byteBuffer3.position());
System.out.println("limit = " + byteBuffer3.limit());
System.out.println("capacity = " + byteBuffer3.capacity());
}
}
执行结果:
对缓冲区写数据的API
//往缓冲区中写入一个字节的数据,游标右移一位。返回缓冲区自身,可进行链式写入。
public abstract ByteBuffer put(byte b);
//往缓冲区数组的某个位置写入一个字节数据,返回缓冲区自身,可进行链式写入。此操作不会使游标移动。
public abstract ByteBuffer put(int index, byte b);
//往缓冲区中添加另一个缓冲区的数据,会读取参数src的position到limit-1下标的数据。
//如果该src的可读数据长度比调用该方法的可写缓冲区的数据长度长的话,就会报错。
public ByteBuffer put(ByteBuffer src);
//往缓冲区里面添加一个字节数组,并可以指定src数组的偏移量和长度。
//添加之后缓冲区的position=oldPsition+length
public ByteBuffer put(byte[] src, int offset, int length);
//往缓冲区里面添加一个字节数组,添加完后position=oldPsition+length
public final ByteBuffer put(byte[] src)
demo:
public static void testPut(){
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
System.out.println("==================>>>初始<<<=====================");
System.out.println("position = " + byteBuffer.position());
System.out.println("limit = " + byteBuffer.limit());
System.out.println("capacity = " + byteBuffer.capacity());
byteBuffer.put((byte) 5);
System.out.println("==================>>>ByteBuffer put(byte b)<<<=====================");
System.out.println("position = " + byteBuffer.position());
System.out.println("limit = " + byteBuffer.limit());
System.out.println("capacity = " + byteBuffer.capacity());
byteBuffer.clear();
byteBuffer.put(2,(byte) 5);
System.out.println("==================>>>put(int index, byte b)<<<=====================");
System.out.println("position = " + byteBuffer.position());
System.out.println("limit = " + byteBuffer.limit());
System.out.println("capacity = " + byteBuffer.capacity());
byteBuffer.clear();
//源缓冲区
ByteBuffer src = ByteBuffer.allocate(5);
src.put("hh".getBytes());
src.flip();
byteBuffer.put(src);
System.out.println("==================>>>put(ByteBuffer src)<<<=====================");
System.out.println("position = " + byteBuffer.position());
System.out.println("limit = " + byteBuffer.limit());
System.out.println("capacity = " + byteBuffer.capacity());
byteBuffer.clear();
byte[] bytes = new byte[5];
byteBuffer.put(bytes,1,3);
System.out.println("==================>>>put(byte[] src, int offset, int length)<<<=====================");
System.out.println("position = " + byteBuffer.position());
System.out.println("limit = " + byteBuffer.limit());
System.out.println("capacity = " + byteBuffer.capacity());
byteBuffer.clear();
byteBuffer.put(bytes);
System.out.println("==================>>>put(byte[] src)<<<=====================");
System.out.println("position = " + byteBuffer.position());
System.out.println("limit = " + byteBuffer.limit());
System.out.println("capacity = " + byteBuffer.capacity());
byteBuffer.clear();
}
结果:
获取缓冲区数据的API
//读取缓冲区的下一字节数据,position右移一位。
public abstract byte get();
//读取缓冲区的指定下标字节数据,position不移动。
public abstract byte get(int index);
//读取缓冲区的数据到dst数组,可以指定读到dst的偏移量和读取数据的长度。
//position右移length。
//返回自身,可链式调用
public ByteBuffer get(byte[] dst, int offset, int length);
//读取缓冲区的数据到dst数组。
//position右移dst.length()。
//返回自身,可链式调用
public ByteBuffer get(byte[] dst);
demo:
public static void testGet(){
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
byteBuffer.put("helloworld".getBytes());
byteBuffer.flip();
System.out.println("==================>>>初始<<<=====================");
System.out.println("position = " + byteBuffer.position());
System.out.println("limit = " + byteBuffer.limit());
System.out.println("capacity = " + byteBuffer.capacity());
byte b = byteBuffer.get();
System.out.println("==================>>>get()<<<=====================");
System.out.println(new String(new byte[]{b}));
System.out.println("position = " + byteBuffer.position());
System.out.println("limit = " + byteBuffer.limit());
System.out.println("capacity = " + byteBuffer.capacity());
byteBuffer.clear();
byteBuffer.put("helloworld".getBytes());
byteBuffer.flip();
byte b1 = byteBuffer.get(2);
System.out.println("==================>>>get(int index)<<<=====================");
System.out.println(new String(new byte[]{b1}));
System.out.println("position = " + byteBuffer.position());
System.out.println("limit = " + byteBuffer.limit());
System.out.println("capacity = " + byteBuffer.capacity());
byteBuffer.clear();
byteBuffer.put("helloworld".getBytes());
byteBuffer.flip();
byte[] bytes = new byte[byteBuffer.remaining()];
byteBuffer.get(bytes);
System.out.println("==================>>>get(byte[] dst, int offset, int length)<<<=====================");
System.out.println(new String(bytes));
System.out.println("position = " + byteBuffer.position());
System.out.println("limit = " + byteBuffer.limit());
System.out.println("capacity = " + byteBuffer.capacity());
byteBuffer.clear();
byteBuffer.put("helloworld".getBytes());
byteBuffer.flip();
byte[] bytes1 = new byte[byteBuffer.remaining()];
byteBuffer.get(bytes1,5,5);
System.out.println("==================>>>get(byte[] dst)<<<=====================");
System.out.println(new String(bytes1,5,5));
System.out.println("position = " + byteBuffer.position());
System.out.println("limit = " + byteBuffer.limit());
System.out.println("capacity = " + byteBuffer.capacity());
byteBuffer.clear();
}
结果:
其他API:
//返回一个只读缓冲区,返回的缓冲区与原本的缓冲区不是同一个缓冲区,是不同的对象。
//与原本缓冲区的数据和元数据都一样,只是他是只读的。
public ByteBuffer asReadOnlyBuffer();
//返回一个int缓冲区。其他类型的也差不多。
public IntBuffer asIntBuffer();
//将字节缓冲区的当前position加后面3位byte数据(共四位)转换成一个int类型返回。
//游标position右移4
public abstract int getInt();
//将字节缓冲区的i下标加后面3位byte数据(共四位)转换成一个int类型返回。
//游标不移动
public int getInt(int i)
//将value值存进缓冲区当前position+后3位,存进去。
//游标position右移4
putInt(int value);
//将value值存进缓冲区index+后3位,存进去。
//游标不移动
putInt(int index, int value)
//上面的操作其他类型的相关操作类似,只是转成的byte数组长度不一样,例如int会转成4,long转成8.//
//返回一个当前ByteBuffer 的副本。是一个新对象。
public ByteBuffer duplicate();
其他六种基本类型的缓冲区API与ByteBuffer的子集类似,有些只是多了解码编码,所以,只要学会ByteBuffer的API差不多等于学会了其他六种基本类型的API。