ByteBuffer正确应用步骤

  1. 向 buffer 写入数据,如调用 int n = channel.read(buffer); 如果返回值 n = -1 ,则代表读取结束。
  2. 调用 flip() 切换至读模式
  3. 从 buffer 读取数据,例如调用 buffer.get();
  4. 调用 clear()compact() 切换至写模式
  5. 循环反复 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 读指针向后走,如果想反复读取数据

  1. 调用 rewind() 办法将 position 从新置为 0
  2. 调用 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 为上次保留的读写地位,设置 positionmark

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();    }