基本概念
java.nio.ByteBuffer
是 Java NIO 中一个核心类, 用于高效处理二进制数据的读写操作。应用于通道(Channel)的I/O操作。作用:
- 数据缓冲:作为内存中的临时存储区,用于在通道Channel(如文件或网络通道)之间传输数据。
- 简化数据处理:支持对基本数据类型(如int、char)的直接读写。
- 高效操作:提供直接内存访问(Direct Buffer),减少数据在JVM堆和本地内存之间的复制开销。
创建 ByteBuffer
(1)堆缓冲区(Heap Buffer)
ByteBuffer buffer = ByteBuffer.allocate(1024); // 在JVM堆上分配
源码:
// java.nio.ByteBuffer
public static ByteBuffer allocate(int capacity) {
if (capacity < 0)
throw new IllegalArgumentException();
return new HeapByteBuffer(capacity, capacity); // 堆分配
}
class HeapByteBuffer extends ByteBuffer
{
HeapByteBuffer(int cap, int lim) {
super(-1, 0, lim, cap, new byte[cap], 0);
/*
hb = new byte[cap]; -- 底层是字节数组
offset = 0; -- 偏移量初始 0
*/
}
(2)直接缓冲区(Direct Buffer)
ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024); // 直接在本地内存分配
源码:
// java.nio.ByteBuffer
public static ByteBuffer allocateDirect(int capacity) {
return new DirectByteBuffer(capacity); // 本地内存分配
}
// ...
(3)包装数组(wrap)
byte[] bytes = new byte[1024];
ByteBuffer wrappedBuffer = ByteBuffer.wrap(bytes); // 包装现有数组
核心属性
ByteBuffer通过四个属性控制读写操作:
- Capacity:缓冲区容量(不可变)。
- Position:当前读写位置,下一个操作的起始索引。
- Limit:可操作数据的最大位置(写模式时等于Capacity,读模式时等于有效数据量)。
- Mark:标记位置,后续可通过
reset()
返回到此位置;
// 继承了 java.nio.Buffer
public abstract class ByteBuffer extends Buffer
implements Comparable<ByteBuffer>{
...
final byte[] hb; // Non-null only for heap buffers
final int offset;
boolean isReadOnly; // Valid only for heap buffers
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;
}
...
}
// 父类 java.nio.Buffer
public abstract class Buffer {
...
// Invariants: mark <= position <= limit <= capacity
private int mark = -1;
private int position = 0;
private int limit;
private int capacity;
// 返回 mark 标记位置
public final Buffer reset() {
int m = mark;
if (m < 0)
throw new InvalidMarkException();
position = m;
return this;
}
...
}
关键方法
切换模式
(1)flip()
:写模式 → 读模式(limit=position,position=0)。
/**
* java.nio.Buffer 父类中实现,
* 使用示例 :
* buf.put(magic); // Prepend header
* in.read(buf); // Read data into rest of buffer
* buf.flip(); // Flip buffer
* out.write(buf); // Write header + data to channel</pre></blockquote>
*/
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
(2)clear()
:读模式 → 写模式(position=0,limit=capacity,数据未删除)。
/**
* Clears this buffer. The position is set to zero, the limit is set to
* the capacity, and the mark is discarded.
*
* <p> Invoke this method before using a sequence of channel-read or
* <i>put</i> operations to fill this buffer. For example:
*
* <blockquote><pre>
* buf.clear(); // Prepare buffer for reading
* in.read(buf); // Read data</pre></blockquote>
*
* <p> This method does not actually erase the data in the buffer, but it
* is named as if it did because it will most often be used in situations
* in which that might as well be the case. </p>
*/
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
(3)rewind()
:重置position为0,用于重新读取数据。
/**
* Rewinds this buffer. The position is set to zero and the mark is
* discarded.
*
* <p> Invoke this method before a sequence of channel-write or <i>get</i>
* operations, assuming that the limit has already been set
* appropriately. For example:
*
* <blockquote><pre>
* out.write(buf); // Write remaining data
* buf.rewind(); // Rewind buffer
* buf.get(array); // Copy data into array</pre></blockquote>
*
* @return This buffer
*/
public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}
读写操作
(1)put(byte b)
和 get()
:相对位置操作(自动移动position)。
java.nio.ByteBuffer
抽象方法:
public abstract class ByteBuffer extends Buffer
implements Comparable<ByteBuffer>{
...
public abstract byte get(); // 抽象方法
public abstract ByteBuffer put(byte b); // 抽象方法
...
}
具体实现类:java.nio.HeapByteBuffer
/ java.nio.DirectByteBuffer
// java.nio.HeapByteBuffer
public byte get() {
return hb[ix(nextGetIndex())];
}
public ByteBuffer put(byte x) {
hb[ix(nextPutIndex())] = x;
return this;
}
final int nextGetIndex() { // package-private
int p = position;
if (p >= limit)
throw new BufferUnderflowException();
position = p + 1;
return p;
}
final int nextPutIndex() { // package-private
int p = position;
if (p >= limit)
throw new BufferOverflowException();
position = p + 1;
return p;
}
(2)put(int index, byte b)
和 get(int index)
:绝对位置操作(不移动position)。
// java.nio.HeapByteBuffer
public byte get(int i) {
return hb[ix(checkIndex(i))];
}
public ByteBuffer put(int i, byte x) {
hb[ix(checkIndex(i))] = x;
return this;
}
// java.nio.Buffer
final int checkIndex(int i) { // package-private
if ((i < 0) || (i >= limit))
throw new IndexOutOfBoundsException();
return i;
}
压缩数据
compact()
:将未读数据复制到缓冲区头部,position设置为剩余数据末尾,继续写入。
java.nio.ByteBuffer
抽象方法:
public abstract ByteBuffer compact();
具体实现类:java.nio.HeapByteBuffer
/ java.nio.DirectByteBuffer
// java.nio.HeapByteBuffer
public ByteBuffer compact() {
// 未读数据复制到缓冲区头部
System.arraycopy(hb, ix(position()), hb, ix(0), remaining());
// position设置为剩余数据末尾
position(remaining());
// limit设置为容量,支持继续写入
limit(capacity());
discardMark();
return this;
}