ByteBuffer正确应用步骤
- 向 buffer 写入数据,如调用
int n = channel.read(buffer);
如果返回值n = -1
,则代表读取结束。 - 调用
flip()
切换至读模式 - 从 buffer 读取数据,例如调用
buffer.get()
; - 调用
clear()
或compact()
切换至写模式 - 循环反复 1 ~ 4 步骤
ByteBuffer 源码
ByteBuffer 主要参数:
// Invariants: mark <= position <= limit <= capacityprivate int mark = -1; // 当调用 mark() 办法后,会把 position 赋值给 markprivate int position = 0; // 示意下一个被读取或者写入的地位private int limit; // 示意第一个不能够被读取或者写入的元素的地位private int capacity; // ByteBuffer,示意能够包容的元素数量
ByteBuffer 一共有两个结构器,且都是 protected 的,因而咱们不能够应用 new 的办法结构对象
// Creates a new buffer with the given mark, position, limit, capacity,// backing array, and array offset//ByteBuffer(int mark, int pos, int lim, int cap, // package-private byte[] hb, int offset){ super(mark, pos, lim, cap); this.hb = hb; this.offset = offset;}// Creates a new buffer with the given mark, position, limit, and capacity//ByteBuffer(int mark, int pos, int lim, int cap) { // package-private this(mark, pos, lim, cap, null, 0);}
咱们能够应用 allocate()
和 allocateDirect()
结构 ByteBuffer
。
public static ByteBuffer allocateDirect(int capacity) { return new DirectByteBuffer(capacity);}public static ByteBuffer allocate(int capacity) { if (capacity < 0) throw createCapacityException(capacity); return new HeapByteBuffer(capacity, capacity);}
外部原理
当通过 allocate()
或 allocateDirect()
结构一个新的 ByteBuffer
后
positon = 0; mark = -1; limit = capacity;
以后模式是写模式时:position
即下一个要写入的地位
切换读写模式
当应用 flip()
切换至读模式时候:limit
设置为下一个写入字节的地位position
为下一个要读取字节的地位,每次读取完后 position++
;
读取的时候如果 position
超过 limit
则抛出 BufferUnderflowException
public Buffer flip() { limit = position; position = 0; mark = -1; return this;}
当应用 clear()
切换至读模式时:
即复原到初始状态,下一个写入字节的地位为 0
该办法相当于革除 ByteBuffer
所有状态
public Buffer clear() { position = 0; limit = capacity; mark = -1; return this;}
还能够应用 compact()
办法切换至读模式,绝对于 clear()
办法,该办法会保留上次未读取完的字节,拷贝到外部字节数组前部,而后使得 position
为未读完字节长度。
重要办法
get()
get()
办法会让 position
读指针向后走,如果想反复读取数据
- 调用
rewind()
办法将position
从新置为 0 - 调用
get(int i)
办法获取索引 i 的内容,它不会挪动读指针
rewind()
把 position
设为 0 ,相当于从头开始读
public Buffer rewind() { position = 0; mark = -1; return this;}
mark() & reset()
留神:rewind() 和 flip() 都会革除 mark 地位
把以后读写地位 position
赋值给 mark
做一个标记,后续能够通过 reset()
办法复原至标记的地位
public Buffer mark() { mark = position; return this;}
转变以后 position
为上次保留的读写地位,设置 position
为 mark
public Buffer reset() { int m = mark; if (m < 0) throw new InvalidMarkException(); position = m; return this;}
字符字节互转
字符串转为字节:
// 字符串间接转为字节数组ByteBuffer buffer1 = ByteBuffer.allocate(16);buffer1.put("hello".getBytes());
// CharsetByteBuffer buffer2 = StandardCharsets.UTF_8.encode("hello");
// warpByteBuffer buffer3 = ByteBuffer.wrap("hello".getBytes());
字节转化为字符:
// ByteBuffer 转 StringString s = StandardCharsets.UTF_8.decode(buffer2).toString();System.out.println(s);
扩散读集中写
当文本文件中为 onetwothree 时,咱们能够一次性读取到大的 ByteBuffer
而后进行宰割,这样会造成效率不高
咱们能够采取扩散读,别离读到对应长度的 ByteBuffer
中去,而后再进行后续解决
try (FileChannel channel = new RandomAccessFile("ScatteringReads.txt", "r").getChannel();) { ByteBuffer b1 = ByteBuffer.allocate(3); ByteBuffer b2 = ByteBuffer.allocate(3); ByteBuffer b3 = ByteBuffer.allocate(5); // 离开读取到对应长度 ByteBuffer 中 channel.read(new ByteBuffer[]{b1, b2, b3}); b1.flip(); b2.flip(); b3.flip(); System.out.println(b1); // one System.out.println(b2); // two System.out.println(b3); // three } catch (IOException e) { e.printStackTrace(); }
当咱们须要把一批数据写入到文件中去,能够别离写,这样会造成 IO 效率低下,能够采纳集中写,汇集到一起,一次性写入到文件中
ByteBuffer b1 = StandardCharsets.UTF_8.encode("hello"); // 5个字节 ByteBuffer b2 = StandardCharsets.UTF_8.encode("world"); // 5个字节 ByteBuffer b3 = StandardCharsets.UTF_8.encode("你好"); // 6个字节 try (FileChannel channel = new RandomAccessFile("GatheringWrites.txt", "rw").getChannel();) { channel.write(new ByteBuffer[]{b1, b2, b3}); } catch (IOException e) { e.printStackTrace(); }