Stack Overflow上59万浏览量的提问:为什么会发生ArrayIndexOutOfBoundsException?

简介: Stack Overflow上59万浏览量的提问:为什么会发生ArrayIndexOutOfBoundsException?

在逛 Stack Overflow 的时候,发现了一些访问量像昆仑山一样高的问题,比如说这个:为什么会发生 ArrayIndexOutOfBoundsException?这样看似简单到不值得一问的问题,访问量足足有 69万+,这不得了啊!说明有不少的初级程序员被这个问题困扰过。实话实说吧,我也有点吃不准为什么。


来回顾一下提问者的问题:


ArrayIndexOutOfBoundsException 究竟意味着什么?我该如何摆脱这个错误。


如果你也曾被这个问题困扰过,或者正在被困扰,就请随我一起来梳理一下问题的答案。打怪进阶喽!


来看这样一段代码,它就可以引起 ArrayIndexOutOfBoundsException。


String[] names = { "沉", "默", "王", "二" };
for (int i = 0; i <= names.length; i++) {
    System.out.println(names[i]);
}

错误的堆栈信息如下所示。


Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 4
    at com.cmower.java_demo.stackoverflow.Cmower1.main(Cmower1.java:7)

抛出这个错误的原因是由于数组使用了非法的下标访问,比如说下标为负数或者大于或者等于数组的长度。


因为数组 names 的长度为 4,但下标的起始位置为 0,而不是 1,导致 names[4] 的时候越界了。这个问题的修正方法蛮简单的,就是把 <= 改为 <。


String[] names = { "沉", "默", "王", "二" };
for (int i = 0; i < names.length; i++) {
    System.out.println(names[i]);
}

当 i 为 4 的时候要跳出 for 循环,names 的最大下标值为 3 而不是 4。


Java 的下标都是从 0 开始编号的(我不确定有没有从 1 开始的编程语言),这和我们平常生活中从 1 开始编号的习惯不同。Java 这样做的原因如下:


Java 是基于 C 语言实现的,而 C 语言的下标是从 0 开始的——这听起来好像是一句废话。真正的原因是下标并不是下标,在指针(C)语言中,它实际上是一个偏移量,距离开始位置的一个偏移量。第一个元素在开头,因此它的偏移量就为 0。


此外,还有另外一种说法。早期的计算机资源比较匮乏,0 作为起始下标相比较于 1 作为起始下标,编译的效率更高。


比如说,10 个元素的数组其结构如下图所示。编号从 0 开始,第 9 个元素将在下标 8 处访问。



为了摆脱 ArrayIndexOutOfBoundsException 的困扰,除了 i < 0; i < names.length;还有一种更值得推荐的做法——使用增强的 for 循环,当我们确定不需要使用下标的时候。


String[] names = { "沉", "默", "王", "二" };
for (String name : names) {
    System.out.println(name);
}

增强的 for 循环,彻底地甩掉了使用数组下标的可能性,也就彻底地摆脱了 ArrayIndexOutOfBoundsException。虽然这只是针对我们开发者来说。


实际上,Java 会把增强的 for 循环语句解释为普通的 for 循环语句,仍然会使用下标。


String[] names = new String[]{"沉", "默", "王", "二"};
String[] var2 = names;
int var3 = names.length;
for(int var4 = 0; var4 < var3; ++var4) {
    String name = var2[var4];
    System.out.println(name);
}

下标 var4 的起始值为 0,var3 为数组的长度;当 var4 自增长为 4 的时候,发现 var4 不小于 var3,于是循环退出。


但不管怎么说,增强的 for 循环的确为我们开发者带来了福音——有效地摆脱了 ArrayIndexOutOfBoundsException。


来对比一下普通的 for 循环和反编译后的增强 for 循环,看看它们之间有什么区别。


for (int i = 0; i < names.length; i++) {
    System.out.println(names[i]);
}
int var3 = names.length;
for(int var4 = 0; var4 < var3; ++var4) {
    String name = var2[var4];
    System.out.println(name);
}

从性能的角度来看,差别主要有两点。


1)增强的 for 循环在遍历之前获取了数组的长度,并保存到了一个临时变量 var3 中,这就避免了每次循环的时候再去获取一次数组长度。


2)增强的 for 循环使用了前置自增 ++var4,而普通的 for 循环使用了后置自增 i++。这两者之间是有一定的差别的,感兴趣的同学可以了解一下。


如果使用的是 JDK8 以上的版本,我们还可以这样遍历数组(不使用下标)。


第一种:使用 List.forEach。


Arrays.asList(names).forEach(System.out::println);

第二种:使用 Stream。


Stream.of(names).forEach(System.out::println);

如果需要对数组执行其他操作,比如说过滤等操作,可以将数组转换为“流”。


这两种做法都需要用到 forEach() 方法,该方法其实是通过增强的 for 循环实现的,源码如下所示。


public void forEach(Consumer<? super E> action) {
    Objects.requireNonNull(action);
    for (E e : a) {
        action.accept(e);
    }
}

说到底,如果想要摆脱 ArrayIndexOutOfBoundsException 的困扰,使用增强的 for 循环来遍历数组就对了。把我们开发者容易疏忽的错误(比如 i <= names.length)交给智能化的编译器来处理,就是最好的办法。



相关文章
|
搜索推荐
带你读《Elastic Stack 实战手册》之70:——4.1.2.实现主流搜索引擎广告置顶显示效果(上)
带你读《Elastic Stack 实战手册》之70:——4.1.2.实现主流搜索引擎广告置顶显示效果(上)
103 0
带你读《Elastic Stack 实战手册》之70:——4.1.2.实现主流搜索引擎广告置顶显示效果(上)
|
自然语言处理 搜索推荐 NoSQL
带你读《Elastic Stack 实战手册》之70:——4.1.2.实现主流搜索引擎广告置顶显示效果(下)
带你读《Elastic Stack 实战手册》之70:——4.1.2.实现主流搜索引擎广告置顶显示效果(下)
105 0
|
存储 人工智能 自然语言处理
实测 ChatGPT 编程效果被其发现,这波我先站队 Stack Overflow
本文对 ChatGPT 解答编程问题的情况进行了测试。测试了不同难度的问题,并对 ChatGPT 的回答结果进行了鉴别。测试结果表明,ChatGPT 在解答简单的编程问题时表现较为出色,但在解决复杂的问题时则不太理想。因此,也总结出了如何更好地向 ChatGPT 提问的方法,但是并不建议没有编程经验的人直接使用 ChatGPT 来解决编程问题。
1591 0
实测 ChatGPT 编程效果被其发现,这波我先站队 Stack Overflow
html+css实战164-溢出显示效果overflow
html+css实战164-溢出显示效果overflow
136 0
html+css实战164-溢出显示效果overflow
|
算法
必考算法之 Top K 问题
大家好,这里是《齐姐聊算法》系列之 Top K 问题。
157 0
必考算法之 Top K 问题
|
Java API Apache
Stack Overflow 最火的一段代码竟然有 Bug...
于是,我看到了下面这个问题:怎样将字节数输出成人类可读的格式?也就是说,怎样将123,456,789字节输出成123.5MB?
Stack Overflow 最火的一段代码竟然有 Bug...
|
机器学习/深度学习 JavaScript 前端开发
stack overflow 问题分类
本教程的目的是带领大家学会如何给 stack overflow 上的问题进行打标签
328 0
|
SQL JavaScript 前端开发
2013年 Stack Overflow 用户调查结果
截止2013年,Stack Overflow社区的月访问量从2150万次增长到了2690万次,访问者分别来自全球的242个国家。为了维持社区的增长,我们做了很多的努力——Careers 2.0已经有了法语和德语两个本地化版本,我们正致力于为整个Stack Overflow网络开发iOS和Android应用,并且Stack Overflow历史上的首次本地化尝试——葡萄牙语版Stack Overflow——已经处于Beta状态。为了让我们更好地为Stack Overflow社区和用户服务,我们每年都会进行一项调查以了解用户心中的期望,用户对网站的使用反馈以及用户心中的其他想法。2013年,我们分
206 0
|
程序员
Stack Overflow 那些让人头大的规矩
stack overflow 今年 stackoverflow.com 已经上线十年,Stack Overflow 可以说是最好的软件类问答网站了,给软件开发人员工作和学习提供了非常大的便利,以至于像我这样的小白,离了 Stack Overflow 简直都不会写程序了。
1365 0