ByteBuffer
HeapByteBuffer:堆内存,读写效率较低,受到 GC 的影响,在被 GC 之后,可能因为移动算法而移动
DirectByteBuffer:直接内存,读写效率高,使用的系统内存,不会受到 GC 影响,在内存中是固定的地址,少一次拷贝,但因需要调用操作系统函数,分配内存效率较低,而且使用不当可能内存泄漏
- position : 缓冲区的位置,下一次读取或写入的位置
- limit:还有多少数据需要写出(调用 read 方法后),或者还有多少空间可以读入数据(调用 write 后),初始和 capacity 一致,调用 flip() 后,limit 会变成 position,而 position 变成 0
- capacity:缓冲区的容量,即给缓冲区开了多大空间
常用方法
- get()
一次读取 buffer 的一个字节,position 向后 1 位,如果是有参构造方法则读取指定位置的数据,不移动 position - clear()
position 置为 0 ,limit = capacity 相当于清空 buffer 了,***重新写,或者重新读***这个 byte[] - put()
往 buffer 写数据,从 position 位置开始覆盖写
// 打印的是 3456789,而不是 1 开始
ByteBuffer allocate = ByteBuffer.allocate(10);
allocate.put(new byte[]{1, 2});
// allocate.flip();
allocate.position(0);
allocate.put(new byte[]{3, 4, 5, 6, 7, 8, 9});
allocate.flip();
while (allocate.hasRemaining()) {
// 一次读1个字节,偏移量每次往后+1
byte b = allocate.get();
System.out.println(b);
}
- position
指定 position - rewinnd
也是将 position 设置为 0 ,mark = -1 - flip()
limit 会变成 position 限制住能读的位置,而 position 变成 0,为写或读做准备,读或写都是从 position 开始的,可以简单理解为切换成读模式,从数组下标第一个开始读
// 写
ByteBuffer allocate = ByteBuffer.allocate(10);
allocate.put(new byte[]{1,2,3,4,5,6,7,8,9,10});
// 如果不调用 flip 方法,会报BufferOverflowException
allocate.flip();
allocate.put(new byte[]{1,2,3,4,5,6,7,8,9,1});
byte b = allocate.get(9);
System.out.println(b);
// 读
ByteBuffer allocate = ByteBuffer.allocate(10);
allocate.put(new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 1});
allocate.flip();
while (allocate.hasRemaining()) {
// 一次读1个字节,偏移量每次往后+1
byte b = allocate.get();
System.out.println(b);
// 每次读都调用该方法,会死循环,一直读第一位
allocate.flip();
}
- compact()
可以理解为切换成写模式,把目前 position 之前的数据删掉,可以***从剩余数据接着往后写***。
但其实是后面剩余的数据复制一份,长度为 limit - position 从 0 开始放,后面的数据不动,比如下面代码打印的是1234534500000(00000 是因为 limit 没限制住,byte[] 里默认是 0 ),而不是12345
ByteBuffer allocate = ByteBuffer.allocate(10);
allocate.put(new byte[]{1, 2, 3, 4, 5});
// 切换成读模式
allocate.flip();
System.out.println(allocate.get());
System.out.println(allocate.get());
System.out.println(allocate.get());
// 将已经读的覆盖掉,接着往后写
allocate.compact();
// position = 0,从0 开始读,但如果调用的是 flip,则不会读两遍的90
allocate.position(0);
while (allocate.hasRemaining()) {
// 一次读1个字节,偏移量每次往后+1
byte b = allocate.get();
System.out.println(b);
}
- mark()
在当前 poisition 位置做标记 - reset()
回到 mark 的 position 位置
字符串和ByteBuffer互转
public static void main(String[] args) {
// 字符串转为 ByteBuffer1,如果容量是5会报错
ByteBuffer buffer = ByteBuffer.allocate(6);
buffer.put("hello1".getBytes());
// 字符串转为 ByteBuffer2,这样转不用担心ByteBuffer容量不够
// 这样生成的 ByteBuffer 的 position 和 limit 都是 0
ByteBuffer hello2 = StandardCharsets.UTF_8.encode("hello2");
// ByteBuffer 转 String
//1、需要 flip 方法
buffer.flip();
String s = StandardCharsets.UTF_8.decode(buffer).toString();
System.out.println(s);
// String 转 ByteBuffer2
// hello2 这样直接可以读,不需要 flip,因为encode方法生成的pos是0
String s1 = new String(hello2.array(), hello2.position(), hello2.limit(), StandardCharsets.UTF_8);
System.out.println(s1);
}
FileChannel
- channel.read(buffer)
读取数据到剩余的缓冲区,其实就是读满当前 buffer position 以后的数据
// 文本是 1234567890abc,打印出来是 abc
private static void read2() {
// 获取 FileChannel
try (FileChannel channel = new FileInputStream("src\file\data1.txt").getChannel()) {
// 准备缓冲区,划出10字节做缓冲区,每次读入10字节的数据到buffer,下次read会从第11个字节开始读
ByteBuffer buffer = ByteBuffer.allocate(10);
// 读取到的字节数,为-1则读结束了
int readLen = channel.read(buffer);
int i = 0;
while (readLen != -1) {
// 打印 buffer 的内容
// 切换到读模式
buffer.flip();
while (buffer.hasRemaining()) {
// 一次读1个字节,偏移量每次往后+1
byte b = buffer.get();
System.out.println((char) b);
}
System.out.println("-------------------------");
i++;
//切换为写模式,其实就是 position = 0,下次会从 11 个字符开始读
buffer.clear();
readLen = channel.read(buffer);
}
} catch (Exception e) {
e.printStackTrace();
}
}