一、BufferUnderflowException 概述
- BufferUnderflowException 是 Java NIO 包中的一个运行时异常,是 RuntimeException 的子类
public class BufferUnderflowException extends RuntimeException {...
}
# 继承关系java.lang.Object-> java.lang.Throwable-> java.lang.Exception-> java.lang.RuntimeException-> java.nio.BufferUnderflowException
- 它发生在尝试从缓冲区读取比实际剩余更多的数据时,即缓冲区位置(position)已经到达或超过限制(limit)
二、常见发生场景
- 读取数据
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
System.out.println("初始化");
System.out.println("pos=" + byteBuffer.position() + ", remaining=" + byteBuffer.remaining() + ", limit=" + byteBuffer.limit());int i1 = byteBuffer.getInt();
System.out.println("第 1 次读取:" + i1);
System.out.println("pos=" + byteBuffer.position() + ", remaining=" + byteBuffer.remaining() + ", limit=" + byteBuffer.limit());int i2 = byteBuffer.getInt();
System.out.println("第 2 次读取:" + i2);
System.out.println("pos=" + byteBuffer.position() + ", remaining=" + byteBuffer.remaining() + ", limit=" + byteBuffer.limit());int i3 = byteBuffer.getInt();
System.out.println("第 3 次读取:" + i3);
System.out.println("pos=" + byteBuffer.position() + ", remaining=" + byteBuffer.remaining() + ", limit=" + byteBuffer.limit());
# 输出结果初始化
pos=0, remaining=10, limit=10
第 1 次读取:0
pos=4, remaining=6, limit=10
第 2 次读取:0
pos=8, remaining=2, limit=10
Exception in thread "main" java.nio.BufferUnderflowException
- 批量读取数据
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
System.out.println("初始化");
System.out.println("pos=" + byteBuffer.position() + ", remaining=" + byteBuffer.remaining() + ", limit=" + byteBuffer.limit());byte[] bytes = new byte[15];
byteBuffer.get(bytes);
System.out.println("读取数据:" + new String(bytes));
System.out.println("pos=" + byteBuffer.position() + ", remaining=" + byteBuffer.remaining() + ", limit=" + byteBuffer.limit());
# 输出结果初始化
pos=0, remaining=10, limit=10
Exception in thread "main" java.nio.BufferUnderflowException
- 调用 flip 方法后,(批量)读取数据
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
System.out.println("初始化");
System.out.println("pos=" + byteBuffer.position() + ", remaining=" + byteBuffer.remaining() + ", limit=" + byteBuffer.limit());String data = "abc";
byteBuffer.put(data.getBytes());
System.out.println("添加数据:" + data);
System.out.println("pos=" + byteBuffer.position() + ", remaining=" + byteBuffer.remaining() + ", limit=" + byteBuffer.limit());byteBuffer.flip();
System.out.println("调用 flip 方法");
System.out.println("pos=" + byteBuffer.position() + ", remaining=" + byteBuffer.remaining() + ", limit=" + byteBuffer.limit());byte[] bytes1 = new byte[2];
byteBuffer.get(bytes1);
System.out.println("第 1 次读取:" + new String(bytes1));
System.out.println("pos=" + byteBuffer.position() + ", remaining=" + byteBuffer.remaining() + ", limit=" + byteBuffer.limit());byte[] bytes2 = new byte[2];
byteBuffer.get(bytes2);
System.out.println("第 2 次读取:" + new String(bytes2));
System.out.println("pos=" + byteBuffer.position() + ", remaining=" + byteBuffer.remaining() + ", limit=" + byteBuffer.limit());
# 输出结果初始化
pos=0, remaining=10, limit=10
添加数据:abc
pos=3, remaining=7, limit=10
调用 flip 方法
pos=0, remaining=3, limit=3
第 1 次读取:ab
pos=2, remaining=1, limit=3
Exception in thread "main" java.nio.BufferUnderflowException
三、避免策略
1、基本介绍
- 总是调用 remaining 方法检查剩余数据是否足够,不同的读取方法对读取长度有不同的要求
方法 | 读取长度(字节) |
---|
byte get() | 1 |
char getChar() | 2 |
short getShort() | 2 |
int getInt() | 4 |
long getLong() | 8 |
float getFloat() | 4 |
double getDouble() | 8 |
ByteBuffer get(byte[] dst) | dst.length |
- 必要时,也可以通过 try catch 捕获异常
2、演示
- 读取数据(调用 remaining 方法检查)
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
System.out.println("初始化");
System.out.println("pos=" + byteBuffer.position() + ", remaining=" + byteBuffer.remaining() + ", limit=" + byteBuffer.limit());int num = 1;
while (true) {if (byteBuffer.remaining() >= 4) {int i1 = byteBuffer.getInt();System.out.println("第 " + num + " 次读取:" + i1);System.out.println("pos=" + byteBuffer.position() + ", remaining=" + byteBuffer.remaining() + ", limit=" + byteBuffer.limit());num++;} else {System.out.println("第 " + num + " 次读取");System.out.println("数据不足,无法读取");break;}
}
# 输出结果初始化
pos=0, remaining=10, limit=10
第 1 次读取:0
pos=4, remaining=6, limit=10
第 2 次读取:0
pos=8, remaining=2, limit=10
第 3 次读取
数据不足,无法读取
- 批量读取数据(调用 remaining 方法检查)
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
System.out.println("初始化");
System.out.println("pos=" + byteBuffer.position() + ", remaining=" + byteBuffer.remaining() + ", limit=" + byteBuffer.limit());byte[] bytes = new byte[15];
if (byteBuffer.remaining() < bytes.length) {System.out.println("数据不足,无法读取");
} else {byteBuffer.get(bytes);System.out.println("读取数据:" + new String(bytes));System.out.println("pos=" + byteBuffer.position() + ", remaining=" + byteBuffer.remaining() + ", limit=" + byteBuffer.limit());
}
# 输出结果初始化
pos=0, remaining=10, limit=10
数据不足,无法读取
- 调用 flip 方法后,(批量)读取数据(调用 remaining 方法检查)
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
System.out.println("初始化");
System.out.println("pos=" + byteBuffer.position() + ", remaining=" + byteBuffer.remaining() + ", limit=" + byteBuffer.limit());String data = "abc";
byteBuffer.put(data.getBytes());
System.out.println("添加数据:" + data);
System.out.println("pos=" + byteBuffer.position() + ", remaining=" + byteBuffer.remaining() + ", limit=" + byteBuffer.limit());byteBuffer.flip();
System.out.println("调用 flip 方法");
System.out.println("pos=" + byteBuffer.position() + ", remaining=" + byteBuffer.remaining() + ", limit=" + byteBuffer.limit());byte[] bytes1 = new byte[2];
if (byteBuffer.remaining() < bytes1.length) {System.out.println("第 1 次读取");System.out.println("数据不足,无法读取");
} else {byteBuffer.get(bytes1);System.out.println("第 1 次读取:" + new String(bytes1));System.out.println("pos=" + byteBuffer.position() + ", remaining=" + byteBuffer.remaining() + ", limit=" + byteBuffer.limit());
}byte[] bytes2 = new byte[2];
if (byteBuffer.remaining() < bytes2.length) {System.out.println("第 2 次读取");System.out.println("数据不足,无法读取");
} else {byteBuffer.get(bytes2);System.out.println("第 2 次读取:" + new String(bytes2));System.out.println("pos=" + byteBuffer.position() + ", remaining=" + byteBuffer.remaining() + ", limit=" + byteBuffer.limit());
}
# 输出结果初始化
pos=0, remaining=10, limit=10
添加数据:abc
pos=3, remaining=7, limit=10
调用 flip 方法
pos=0, remaining=3, limit=3
第 1 次读取:ab
pos=2, remaining=1, limit=3
第 2 次读取
数据不足,无法读取
- 通过 try catch 捕获异常
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
System.out.println("初始化");
System.out.println("pos=" + byteBuffer.position() + ", remaining=" + byteBuffer.remaining() + ", limit=" + byteBuffer.limit());try {byte[] bytes = new byte[15];byteBuffer.get(bytes);System.out.println("读取数据:" + new String(bytes));System.out.println("pos=" + byteBuffer.position() + ", remaining=" + byteBuffer.remaining() + ", limit=" + byteBuffer.limit());
} catch (BufferUnderflowException e) {e.printStackTrace();System.out.println("读取数据失败");System.out.println("pos=" + byteBuffer.position() + ", remaining=" + byteBuffer.remaining() + ", limit=" + byteBuffer.limit());
}
# 输出结果初始化
pos=0, remaining=10, limit=10
java.nio.BufferUnderflowException
读取数据失败
pos=0, remaining=10, limit=10