五分钟学Java:为什么会发生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)交给智能化的编译器来处理,就是最好的办法。


好了各位读者朋友们,以上就是本文的全部内容了。能看到这里的都是最优秀的程序员,升职加薪就是你了👍。如果觉得不过瘾,我把本系列文章做了汇总,这里推荐给大家。

五分钟学Java:打印数组最优雅的方式是什么?

五分钟学Java:为什么不应该使用Java的原始类型?

五分钟学Java:为什么会发生ArrayIndexOutOfBoundsException?

五分钟学Java:Java 到底是值传递还是引用传递?

五分钟学Java:如何比较 Java 的字符串?

五分钟学Java:什么是 NullPointerException?

谢谢大家的阅读,原创不易,喜欢就点个赞,这将是我最强的写作动力

发布了615 篇原创文章 · 获赞 9638 · 访问量 267万+
展开阅读全文

Runnable 中抛出java.lang.ArrayIndexOutOfBoundsException: -1

04-23

[已解决] SimpleDateFormat.format并发调用会有问题private SimpleDateFormat.StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) 里面用了calendar.setTime(date);这玩意是全局的,后面还会调用它,所以并发时候会出问题。 Random中protected Random.next(int bits);采用AtomicLong的CAS操作并发时候不会出问题。 ------------------------------------------------------------------------------ 每个Task有自己的ArrayList实例,在多线程情况下每个ArrayList实例应该不存在并发写入啊,为没什么还报ArrayIndexOutOfBoundsException: -1 求助大神,网上找了好久没结果。 ``` import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import static java.lang.System.out; public class TestArrayList { public static void main(String[] args){ TestArrayList t= new TestArrayList(); t.testInsertRows(); } private static int millisecondOf1Day= 1000*3600*24; private static int millisecondOf5Min= 1000*60*5; private static final SimpleDateFormat sdf= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private final ExecutorService pool= Executors.newFixedThreadPool(8); public void testInsertRows(){ Random random= new Random(); int taskId= 0; for(int i=0; i< 8; i++){ taskId++; pool.submit(new Task(taskId, 10000, null)); } out.println("all submited!"); } static class Task implements Runnable{ private Integer id; private List<Bean> list; private int rowCount; private int count=0; private Random random; public Task(Integer id, Integer rowCount, Random random){ this.id= id; this.list= new ArrayList<>(rowCount+1); this.rowCount= rowCount; // this.random= random; this.random= new Random(); } @Override public void run() { out.println("task:"+id+" is running! "+Thread.currentThread()); try{ for(int j=0; j< rowCount; j++ ){ Long randomTime= random.nextLong(); Date validityDate= new Date(randomTime- millisecondOf1Day), startDate= new Date(randomTime), endDate= new Date(randomTime+ 2*millisecondOf1Day); Bean bean = new Bean(); bean.setField0("K0000033"); bean.setField1("0001"); bean.setField2(sdf.format(validityDate)); bean.setField3(new Double(0.0).toString()); bean.setField4(sdf.format(startDate)); bean.setField5(sdf.format(endDate)); list.add(bean); } out.println("list is prepared in thread:"+id); Thread.sleep(millisecondOf5Min);// out.println(count+" rows has been inserted in thread:"+id); }catch (Exception e) { synchronized (out) { out.println("task:"+id+" in error"+Thread.currentThread()); e.printStackTrace(); out.println("task:"+id+" error info print completed"); } } } } } class Bean{ private String field0; private String field1; private String field2; private String field3; private String field4; private String field5; private String field6; /** * @return the field0 */ public String getField0() { return field0; } /** * @param field0 the field0 to set */ public void setField0(String field0) { this.field0 = field0; } /** * @return the field1 */ public String getField1() { return field1; } /** * @param field1 the field1 to set */ public void setField1(String field1) { this.field1 = field1; } /** * @return the field2 */ public String getField2() { return field2; } /** * @param field2 the field2 to set */ public void setField2(String field2) { this.field2 = field2; } /** * @return the field3 */ public String getField3() { return field3; } /** * @param field3 the field3 to set */ public void setField3(String field3) { this.field3 = field3; } /** * @return the field4 */ public String getField4() { return field4; } /** * @param field4 the field4 to set */ public void setField4(String field4) { this.field4 = field4; } /** * @return the field5 */ public String getField5() { return field5; } /** * @param field5 the field5 to set */ public void setField5(String field5) { this.field5 = field5; } /** * @return the field6 */ public String getField6() { return field6; } /** * @param field6 the field6 to set */ public void setField6(String field6) { this.field6 = field6; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "Bean [field0=" + field0 + ", field1=" + field1 + ", field2=" + field2 + ", field3=" + field3 + ", field4=" + field4 + ", field5=" + field5 + ", field6=" + field6 + "]"; } } ``` task:1 is running! Thread[pool-1-thread-1,5,main] task:4 is running! Thread[pool-1-thread-4,5,main] task:3 is running! Thread[pool-1-thread-3,5,main] task:2 is running! Thread[pool-1-thread-2,5,main] task:5 is running! Thread[pool-1-thread-5,5,main] task:6 is running! Thread[pool-1-thread-6,5,main] all submited! task:7 is running! Thread[pool-1-thread-7,5,main] task:8 is running! Thread[pool-1-thread-8,5,main] task:7 in errorThread[pool-1-thread-7,5,main] java.lang.ArrayIndexOutOfBoundsException: -1 at java.util.Calendar.getDisplayName(Unknown Source) at java.text.SimpleDateFormat.subFormat(Unknown Source) at java.text.SimpleDateFormat.format(Unknown Source) at java.text.SimpleDateFormat.format(Unknown Source) at java.text.DateFormat.format(Unknown Source) at com.haimian.test.concurrent.TestArrayList$Task.run(TestArrayList.java:64) at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) at java.util.concurrent.FutureTask.run(Unknown Source) at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.lang.Thread.run(Unknown Source) task:7 error info print completed task:8 in errorThread[pool-1-thread-8,5,main] java.lang.ArrayIndexOutOfBoundsException: -1 at java.util.Calendar.getDisplayName(Unknown Source) at java.text.SimpleDateFormat.subFormat(Unknown Source) at java.text.SimpleDateFormat.format(Unknown Source) at java.text.SimpleDateFormat.format(Unknown Source) at java.text.DateFormat.format(Unknown Source) at com.haimian.test.concurrent.TestArrayList$Task.run(TestArrayList.java:65) at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) at java.util.concurrent.FutureTask.run(Unknown Source) at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.lang.Thread.run(Unknown Source) task:8 error info print completed task:4 in errorThread[pool-1-thread-4,5,main] java.lang.ArrayIndexOutOfBoundsException: -1 at java.util.Calendar.getDisplayName(Unknown Source) at java.text.SimpleDateFormat.subFormat(Unknown Source) at java.text.SimpleDateFormat.format(Unknown Source) at java.text.SimpleDateFormat.format(Unknown Source) at java.text.DateFormat.format(Unknown Source) at com.haimian.test.concurrent.TestArrayList$Task.run(TestArrayList.java:62) at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) at java.util.concurrent.FutureTask.run(Unknown Source) at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.lang.Thread.run(Unknown Source) task:4 error info print completed task:1 in errorThread[pool-1-thread-1,5,main] java.lang.ArrayIndexOutOfBoundsException: -1 at java.util.Calendar.getDisplayName(Unknown Source) at java.text.SimpleDateFormat.subFormat(Unknown Source) at java.text.SimpleDateFormat.format(Unknown Source) at java.text.SimpleDateFormat.format(Unknown Source) at java.text.DateFormat.format(Unknown Source) at com.haimian.test.concurrent.TestArrayList$Task.run(TestArrayList.java:64) at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) at java.util.concurrent.FutureTask.run(Unknown Source) at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.lang.Thread.run(Unknown Source) task:1 error info print completed task:5 in errorThread[pool-1-thread-5,5,main] java.lang.ArrayIndexOutOfBoundsException: -1 at java.util.Calendar.getDisplayName(Unknown Source) at java.text.SimpleDateFormat.subFormat(Unknown Source) at java.text.SimpleDateFormat.format(Unknown Source) at java.text.SimpleDateFormat.format(Unknown Source) at java.text.DateFormat.format(Unknown Source) at com.haimian.test.concurrent.TestArrayList$Task.run(TestArrayList.java:65) at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) at java.util.concurrent.FutureTask.run(Unknown Source) at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.lang.Thread.run(Unknown Source) task:5 error info print completed task:3 in errorThread[pool-1-thread-3,5,main] java.lang.ArrayIndexOutOfBoundsException: -1 at java.util.Calendar.getDisplayName(Unknown Source) at java.text.SimpleDateFormat.subFormat(Unknown Source) at java.text.SimpleDateFormat.format(Unknown Source) at java.text.SimpleDateFormat.format(Unknown Source) at java.text.DateFormat.format(Unknown Source) at com.haimian.test.concurrent.TestArrayList$Task.run(TestArrayList.java:64) at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) at java.util.concurrent.FutureTask.run(Unknown Source) at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.lang.Thread.run(Unknown Source) task:3 error info print completed task:6 in errorThread[pool-1-thread-6,5,main] java.lang.ArrayIndexOutOfBoundsException: -1 at java.util.Calendar.getDisplayName(Unknown Source) at java.text.SimpleDateFormat.subFormat(Unknown Source) at java.text.SimpleDateFormat.format(Unknown Source) at java.text.SimpleDateFormat.format(Unknown Source) at java.text.DateFormat.format(Unknown Source) at com.haimian.test.concurrent.TestArrayList$Task.run(TestArrayList.java:64) at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) at java.util.concurrent.FutureTask.run(Unknown Source) at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.lang.Thread.run(Unknown Source) task:6 error info print completed list is prepared in thread:2 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览