关于ArrayIndexOutOfBoundException与IndexOutOfBoundsException的异常疑问
这是个很有意思的观察!其实背后是ArrayList的实现细节(以及JDK版本差异)在起作用,咱们一步步拆解:
先看你给出的两段代码
第一段:正数越界触发IndexOutOfBoundsException
public static void main(String[] args) { List<Integer> elements = new ArrayList<>(); elements.add(10); int firstElmnt = elements.get(1); System.out.println(firstElmnt); }
当调用get(1)时,ArrayList的当前size是1。在旧版JDK(比如JDK8及以前)的get方法里,会先执行rangeCheck(index)检查:
private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }
这里只检查了索引是否大于等于集合大小,1 >= 1成立,所以直接抛出IndexOutOfBoundsException,不会走到访问底层数组的步骤。
第二段:负数索引触发ArrayIndexOutOfBoundsException
public static void main(String[] args) { List<Integer> elements = new ArrayList<>(); elements.add(10); int firstElmnt = elements.get(-1); System.out.println(firstElmnt); }
调用get(-1)时,rangeCheck(index)检查-1 >= 1?显然不成立,所以跳过了上层检查,直接进入elementData(index)方法访问底层数组:
E elementData(int index) { return (E) elementData[index]; }
而Java数组本身不允许负数索引,这时候访问elementData[-1]就会触发数组的原生异常ArrayIndexOutOfBoundsException。
注意:这不是通用规则!
在JDK9及以后的版本里,ArrayList的get方法实现已经优化了:它改用Objects.checkIndex(index, size)来做索引检查,这个方法会同时验证索引是否小于0和是否大于等于集合大小,并且统一抛出IndexOutOfBoundsException。也就是说,新版本里不管是正数越界还是负数索引,都会抛出同一种异常。
总结
你看到的差异是旧版JDK中ArrayList的检查逻辑漏洞导致的:上层只拦截了“索引过大”的情况,没拦截“索引为负”的情况,让后者直接触发了底层数组的异常。并不是“负数索引必然触发ArrayIndexOutOfBoundsException”的通用规则哦。
内容的提问来源于stack exchange,提问作者Priya Rastogi

