真正可靠的边界保护是在访问前验证索引合法性,而非依赖IndexOutOfBoundsException:SafeArray通过构造维护size、入口统一预检、迭代与批量操作单独校验三步实现。

直接靠抛异常来“确保”索引合法,不是保护,是放弃控制。IndexOutOfBoundsException 是运行时失败信号,不是边界防护机制。真正可靠的边界保护,是在访问发生前就完成验证,把非法索引挡在门外。
别让异常替你做判断
有人误以为“反正会抛 IndexOutOfBoundsException,我 catch 住就行”,这是高风险做法:
- 异常开销大,频繁触发会显著拖慢性能
- 掩盖逻辑缺陷——本该提前拒绝的索引,不该靠崩溃后补救
- 无法区分是用户输入错误、算法 bug 还是并发干扰,调试困难
- 对自定义容器而言,暴露底层异常还破坏封装性,调用方被迫处理实现细节
自定义容器中真正的边界保护三步法
以实现一个简单泛型数组容器 SafeArray<t></t> 为例,合法索引范围始终是 0 ≤ index :
-
构造与状态维护阶段:size 字段严格由 add/remove 操作更新,不对外暴露;内部不缓存 length 副本,始终用
elements.length和this.size两个真实值协同校验 -
访问入口统一预检:所有带索引的方法(
get(index)、set(index, value)、removeAt(index))开头第一行就是:if (index = this.size) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + this.size); -
迭代器与批量操作单独守门:比如
subList(from, to)必须同时检查from ≥ 0、to ≤ size、from ≤ to;循环遍历方法(如forEachIndexed)内部用for (int i = 0; i ,而非依赖外部传入的“范围”参数
比检查更进一步:用类型和设计消除越界可能
静态防护优于动态检查:
- 对只读场景,提供
first()、last()、getOrNull(index)(返回 null 而非异常),把“是否越界”的决策权交给调用方,而不是强制中断 - 用
IntRange或自定义Index类型封装索引,构造时即校验,让非法值根本造不出来(例如new Index(5, array.size)在越界时直接失败) - 对二分查找、滑动窗口等算法逻辑,把边界计算(如
right = size - 1)、更新(left = mid + 1)和循环条件(while (left )作为整体设计验证,而不是零散加 if
异常该在哪出现?只在兜底和诊断时
IndexOutOfBoundsException 的合理位置只有两个:
- 开发/测试阶段的断言开关:开启时快速暴露逻辑漏洞,关闭时不参与生产逻辑
-
最终访问点的明确报错:比如用户通过 API 传入了明显错误的索引(如 HTTP 请求参数
?index=-100),此时抛出带上下文信息的异常,便于日志追踪和前端提示
它不该是你容器内部数据流转的“交通灯”,而应是路标尽头那块写着“此路不通”的警示牌。

















