天天看點

Scanner 踩坑:java.util.NoSuchElementException

使用 scanner 過後,感覺應該像讀取檔案之後一樣将它關閉,是以調用 close() 方法。在下一次需要輸入時,再重新建立 scanner 對象讀取輸入。好像沒什麼問題。

然而運作時抛出異常如下。難道同一程式中 scanner 隻能建立一次嗎?

Scanner 踩坑:java.util.NoSuchElementException

結合源碼分析,真正的原因在 ​<code>​system.in​</code>​。我們來看 ​<code>​system.in​</code>​ 在 scanner 對象中的走向。

首先,從 ​<code>​system​</code>​ 類源碼中可以知道 ​<code>​system.in​</code>​ 是不可變的靜态資源,即隻有一份。從源碼說明中可以知道,它作為 ​<code>​inputstream​</code>​ 會在使用前被系統自動打開并連接配接到輸入源(如鍵盤),那也就是說它隻能被使用一次,如果被關閉就無法再使用了。

Scanner 踩坑:java.util.NoSuchElementException

然後再看,在通過 ​<code>​new scanner(system.in)​</code>​ 建立 scanner 對象時,scanner 的一個構造器被調用,

Scanner 踩坑:java.util.NoSuchElementException

這個構造器調用了另一個構造器,并傳入了 ​<code>​system.in​</code>​ 作為 ​<code>​source​</code>​ 參數。在這一個構造器中,scanner 對象的成員變量 ​<code>​source​</code>​ 被确定下來。

Scanner 踩坑:java.util.NoSuchElementException

最後,當我們調用 ​<code>​close()​</code>​ 時,​<code>​source​</code>​ 的 ​<code>​close()​</code>​ 方法會被調用,實際被關閉的就是 ​<code>​system.in​</code>​ 這一靜态資源。

Scanner 踩坑:java.util.NoSuchElementException

是以當再一次建立 scanner 對象,傳入的是一個已經被關閉了的 ​<code>​system.in​</code>​,它此時已經無法讀取輸入,是以在嘗試讀取操作時會抛出異常。

既然找到了原因:​<code>​input.close()​</code>​ 使 ​<code>​system.in​</code>​ 過早地關閉了,那解決辦法自然有了:最後再調用 ​<code>​close()​</code>​。可以有三種方式:

在需要時随時建立一個 scanner 對象傳入 <code>system.in</code>,但在使用後不馬上調用 <code>close()</code>,而是在所有擷取輸入操作結束後再調用 <code>close()</code>;

隻建立一個 scanner 對象,将其作為參數傳入需要擷取輸入的方法中,在所有擷取輸入操作結束後再調用 <code>close()</code>;

建立一個靜态 scanner 對象,當不再需要使用該靜态 scanner 對象擷取輸入後調用 <code>close()</code>。

第三種方式實作起來更加簡潔:

Scanner 踩坑:java.util.NoSuchElementException