Stack Overflow上59萬瀏覽量的提問:爲何會發生ArrayIndexOutOfBoundsException?

在逛 Stack Overflow 的時候,發現了一些訪問量像崑崙山同樣高的問題,好比說這個:爲何會發生 ArrayIndexOutOfBoundsException?這樣看似簡單到不值得一問的問題,訪問量足足有 69萬+,這不得了啊!說明有很多的初級程序員被這個問題困擾過。實話實說吧,也有點吃不許爲何。java

來回顧一下提問者的問題:程序員

ArrayIndexOutOfBoundsException 究竟意味着什麼?我該如何擺脫這個錯誤。編程

若是你也曾被這個問題困擾過,或者正在被困擾,就請隨我一塊兒來梳理一下問題的答案。打怪進階嘍!數組

來看這樣一段代碼,它就能夠引發 ArrayIndexOutOfBoundsExceptionbash

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] 的時候越界了。這個問題的修正方法蠻簡單的,就是把 <= 改成 <ui

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

i 爲 4 的時候要跳出 for 循環,names 的最大下標值爲 3 而不是 4。spa

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)交給智能化的編譯器來處理,就是最好的辦法。


好了各位讀者朋友們,以上就是本文的所有內容了。能看到這裏的都是人才,二哥必需要爲你點個贊👍。若是以爲不過癮,還想看到更多,我再推薦幾篇給你們。

Stack Overflow上188萬瀏覽量的提問:Java 究竟是值傳遞仍是引用傳遞? Stack Overflow 上 370萬瀏覽量的一個問題:如何比較 Java 的字符串? Stack Overflow 上 250萬瀏覽量的一個問題:什麼是 NullPointerException

養成好習慣!若是以爲這篇文章有點用的話,求點贊、求關注、求分享、求留言,這將是我寫下去的最強動力!

相關文章
相關標籤/搜索