BufferUnderflowException是被动提示越界读取的运行时异常,非防御机制;真正防护需在调用get()等方法前主动校验buffer.remaining()≥所需字节数。

1. 读取前显式检查 remaining() 是否充足
这是最直接、最推荐的做法。每次批量读取(如 `get(byte[])`、`getInt()`、`getLong()`)前,先确认缓冲区还剩多少字节,是否满足所需长度:
- `buffer.remaining() >= N` —— 读取 N 字节前必须成立
- `buffer.remaining() >= 4` —— 调用 `getInt()` 前必须成立
- `buffer.remaining() >= 8` —— 调用 `getLong()` 前必须成立
示例:
if (buffer.remaining() < 4) {
throw new IllegalArgumentException("Insufficient data: expected 4 bytes for int, got " + buffer.remaining());
}
int value = buffer.getInt(); // 安全
2. 封装安全读取工具方法
避免重复写校验逻辑,封装成带校验的工具方法,统一处理不足情况(可抛自定义异常、返回 Optional 或填充默认值):
public static int safeGetInt(ByteBuffer buffer) {
if (buffer.remaining() < 4) {
throw new BufferUnderflowException(); // 或自定义异常如 DataCorruptionException
}
return buffer.getInt();
}
public static byte[] safeGetBytes(ByteBuffer buffer, int len) {
if (buffer.remaining() < len) {
throw new IllegalArgumentException("Cannot read " + len + " bytes: only " + buffer.remaining() + " available");
}
byte[] dst = new byte[len];
buffer.get(dst);
return dst;
}
3. 使用 mark()/reset() 配合预检(适用于动态长度场景)
当你要先读一个长度字段(如 `short len`),再根据它读后续数据时,容易因长度被篡改或损坏导致越界。此时可用标记+回退机制做二次校验:
立即学习“Java免费学习笔记(深入)”;
buffer.mark();
if (buffer.remaining() < 2) throw new IllegalArgumentException("No length field");
short len = buffer.getShort();
// 检查 len 是否合理(防负数、过大)
if (len < 0 || len > 65535 || buffer.remaining() < len) {
throw new IllegalArgumentException("Invalid payload length: " + len);
}
// 此时才真正读取
byte[] payload = new byte[len];
buffer.get(payload); // 安全
注意:`mark()` 不是必须的,但若你想在失败时保留原始位置(比如日志后跳过该条记录),它很有用。
4. 启用缓冲区只读/限制(防御性编程补充)
在接收外部数据(如网络字节流)后,立即用 `ByteBuffer.slice()` 或 `limit()` 设定可信边界,避免后续误操作扩大可读范围:
- 收到 1024 字节数据 → 创建 `ByteBuffer.wrap(data, 0, actualLength)`,确保 `limit == actualLength`
- 解析协议头后,用 `buffer.slice()` 截取有效载荷子缓冲区,并设好新 limit
这样即使业务代码有 bug,也不会越过最初约定的数据边界。
不复杂但容易忽略


















