首页 > 代码库 > scanner使用中遇见的问题

scanner使用中遇见的问题

最近在学习的过程中遇见一个问题,问题不难但还是需要去认真对待。

先看看我写的源代码

	public static void main(String[] args){	
		for(;;){
			Scanner in = new Scanner(System.in);
			System.out.println("-----");
			int age = in.nextInt();
			System.out.println("------");
			in.close();
			System.out.println(age>100);
		}	
	}
在这段代码中,当第一次输入是不会有错,能正常运行;然后第二次循环报错。

报出来的错误为:

Exception in thread "main" java.util.NoSuchElementException
at java.util.Scanner.throwFor(Scanner.java:838)
at java.util.Scanner.next(Scanner.java:1461)
at java.util.Scanner.nextInt(Scanner.java:2091)
at java.util.Scanner.nextInt(Scanner.java:2050)
at day01.Demo01.main(Demo01.java:11)

看到这,感觉到相当的郁闷。当我们把in.close()这段代码给注释掉的话,那这段代码可以无限的循环下去。

但是从这段代码的逻辑来看,似乎没错,但机器给我们报出了错。这就需要我们去找到错误的。

根据报出来的错,我们看到是int age = in.nextInt();这行代码出错。


从这报出来的错误中来分析,简要的说,就是前后两次实例化的参数System.in是用一个对象,是InputStreamReader对象,每个该对象包含一个StreamDecoder 实例 sd,private final StreamDecoder sd;  

而in.close()方法为

public void close() {   
       if (closed)   
           return;   
       if (source instanceof Closeable) {   
           try {   
               ((Closeable)source).close();   
           } catch (IOException ioe) {   
               lastException = ioe;   
           }   
       }   
       sourceClosed = true;   
       source = null;   
       closed = true;   
   }   


当执行到 ((Closeable)source).close();就会进入InputStreamReader的close()方法:

public void close() throws IOException {   
                sd.close();   
 } 

这里的sd就是上面提到的InputStreamReader对象,(又查了StreamDecoder 源代码,但没更深入下去),此时sd已关闭。

当执行如错误产生代码的第11行代码 in.nextInt()时,

public int nextInt(int radix) {
        // Check cached result
        if ((typeCache != null) && (typeCache instanceof Integer)
	    && this.radix == radix) {
            int val = ((Integer)typeCache).intValue();
            useTypeCache();
            return val;
        }
        setRadix(radix);
        clearCaches();
        // Search for next int
        try {
            String s = next(integerPattern());
            if (matcher.group(SIMPLE_GROUP_INDEX) == null)
                s = processIntegerToken(s);
            return Integer.parseInt(s, radix);
        } catch (NumberFormatException nfe) {
            position = matcher.start(); // don't skip bad token
            throw new InputMismatchException(nfe.getMessage());
        }
    }
其中调用了next()方法

public String next(Pattern pattern) {
        ensureOpen();
        if (pattern == null)
            throw new NullPointerException();

        // Did we already find this pattern?
        if (hasNextPattern == pattern)
            return getCachedResult();
        clearCaches();

        // Search for the pattern
        while (true) {
            String token = getCompleteTokenInBuffer(pattern);
            if (token != null) {
                matchValid = true;
                skipped = false;
                return token;
            }
            if (needInput)
                readInput();
            else
                throwFor();
        }
    }

异常是从方法throwFor();中抛出,而异常的来源是readInput()方法

private void readInput() {   
        if (buf.limit() == buf.capacity())   
            makeSpace();   
 
        // Prepare to receive data   
        int p = buf.position();   
        buf.position(buf.limit());   
        buf.limit(buf.capacity());   
 
        int n = 0;   
        try {   
            n = source.read(buf);   
        } catch (IOException ioe) {   
            lastException = ioe;   
            n = -1;   
        }   
 
        if (n == -1) {   
            sourceClosed = true;   
            needInput = false;   
        }   
 
        if (n > 0)   
            needInput = false;   
 
        // Restore current position and limit for reading   
        buf.limit(buf.position());   
        buf.position(p);   
    }  

当执行到12行source.read()时,source是Reader类

public int read(java.nio.CharBuffer target) throws IOException {   
    int len = target.remaining();   
    char[] cbuf = new char[len];   
    int n = read(cbuf, 0, len);   
    if (n > 0)   
        target.put(cbuf, 0, n);   
    return n;   
} 

在执行InputStreamReader的read方法

public int read(char cbuf[], int offset, int length) throws IOException {   
                 return sd.read(cbuf, offset, length);   
}  

而该InputStreamReader实际上就是System.in,而之前的close()方法已经将sd关闭了,此处再次实行read方法,则抛出IOException,然后层层捕获,最终抛出.NoSuchElementException

 

以上错误归根揭底,最主要的原因是System.in输入流已经关闭。