应提前校验索引合法性而非依赖运行时异常:循环用i

怎么提前发现数组越界风险
Java 不会在编译期检查 array[i] 的索引是否合法,只有运行时才抛 ArrayIndexOutOfBoundsException。所以关键不是“捕获它”,而是“别让它发生”。
常见错误现象:循环里用 i 当终止条件;从 1 开始遍历却忘了改下标;传入外部参数当索引没校验。
使用场景:处理用户输入的索引、解析 CSV 行字段、分页计算起始位置、动态拼接数组等。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 所有数组访问前加显式校验:
if (i >= 0 && i - 循环用
for (int i = 0; i ,别手抖写成 <code> - 外部传入的索引(比如 HTTP 请求里的
index参数)必须先校验再用,不能依赖 try-catch “兜底”
为什么不该用 try-catch 捕获 ArrayIndexOutOfBoundsException
这个异常是 RuntimeException 子类,设计上就表示“程序逻辑错误”,不是可预期的业务异常。靠捕获它来控制流程,等于把 bug 当功能用。
性能影响:抛异常开销大,JVM 要生成栈轨迹,频繁触发会明显拖慢吞吐。 可维护性问题:掩盖真实缺陷,下次有人改逻辑时更难定位越界源头。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 禁止在正常业务路径中写
catch (ArrayIndexOutOfBoundsException e) - 单元测试里可以故意触发它来验证边界校验逻辑,但生产代码里不许出现
- 如果真遇到“不确定长度”的场景(比如解析协议头),优先用
List或Optional.ofNullable()封装,而不是硬扛数组
替代数组的更安全选择有哪些
不是所有地方都非用数组不可。很多越界问题源于过早选择了低阶数据结构。
使用场景:需要动态增删、不确定大小、多线程共享、或索引来源不可信。
参数差异与兼容性:
-
ArrayList的get(int index)同样会抛IndexOutOfBoundsException,但它提供了size()和isEmpty()更易组合校验 -
java.util.Arrays.asList()返回的 List 是固定大小的,修改仍可能出错,别误以为“转成 List 就安全了” - JDK 21+ 的
SequencedCollection接口开始统一边界行为,但目前主流项目还用不上
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 新代码优先用
ArrayList或ImmutableList(Guava),配合Math.min(i, list.size() - 1)做容错取值 - 配置类、DTO 字段尽量用
List<string></string>而非String[] - 如果必须用数组(比如 JNI、高性能计算),把校验逻辑抽成工具方法:
ArrayUtil.safeGet(array, i, defaultValue)
调试时怎么快速定位越界位置
堆栈信息里只显示抛异常的那行,但真正的问题往往在上游——比如索引是上一个方法算错的,或者被多次传递后失真。
常见错误现象:日志只打印 Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5,但看不出谁传的 5。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 在关键索引赋值点加断点或日志,例如:
log.debug("calculated index: {}, array length: {}", i, array.length) - 使用 IDE 的 “Evaluate Expression” 功能,在异常断点处直接查
array.length和i的实时值 - 如果是多线程环境,注意
array.length可能被并发修改,此时越界可能是竞态导致,不是单纯逻辑错
数组越界从来不是“运气不好”,而是某个环节放弃了对数据边界的主动控制。最危险的不是报错,是它偶尔不报错——比如刚好访问到已分配但未初始化的内存区域,结果返回垃圾值,问题延后暴露。


















