java.util.NoSuchElementException

问题引入

Java商店作业不同函数里需要获取用户输入,用Scanner的时候,出现了异常java.util.NoSuchElementException

作业中代码模式如下,func1func2中都使用Scanner并关闭它。然后在main中依次调用func1func2func2产生异常。说func1func2其实不合适,应该加个括号……..懒得加了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//Demo.java
import java.util.Scanner;

class Test{
void func1() {
Scanner sc=new Scanner(System.in);

//输出用户输入,替代作业里的使用Scanner
System.out.print("Func1请输入内容:");
System.out.println("Func1输出"+sc.next());

//关闭Scanner
sc.close();
}

void func2() {
Scanner sc=new Scanner(System.in);

//输出用户输入,替代作业里的使用Scanner
System.out.print("Func2请输入内容:");
System.out.println("Func2输出"+sc.nextInt());

//关闭Scanner
sc.close();
}
}
public class Demo{
public static void main(String[] args) {
Test t=new Test();
t.func1();
t.func2();
}
}

/*Console输出如下:

Func1请输入内容:小姐,请问你喜欢吃青椒吗?
Func1输出:小姐,请问你喜欢吃青椒吗?
Func2请输入内容:Exception in thread "main" java.util.NoSuchElementException
at java.base/java.util.Scanner.throwFor(Unknown Source)
at java.base/java.util.Scanner.next(Unknown Source)
at java.base/java.util.Scanner.nextInt(Unknown Source)
at java.base/java.util.Scanner.nextInt(Unknown Source)
at Test.func2(Demo.java:21)
at Demo.main(Demo.java:31)


*/

异常产生原因

func1sc.close();语句关闭了Scannerfunc2中使用Scanner产生异常

因为System.inSystem类的静态成员,所以不同Scanner对象内的in是同一个in`

func1func2中都用System.in创建了Scanner

func1打开Scanner后将其关闭,这里间接地将System.in也关闭了

func1结束后运行func2,这时再调用nextInt,在System.in已经关闭了的情况下,不能读取到任何数据,就会产生 java.util.NoSuchElementException

解决方法

系统资源一旦释放就不能再开启了,所以只有确定不在使用系统的时候,才能将流关闭

所以应该在整个程序结束时释放Scanner等资源,而不是某个函数中每次使用Scanner等资源后都释放一次

问题引入中的代码只是个模式,上边的两句话用在作业实际代码里就好了

代码分析

Scanner()

创建Scanner对象代码为Scanner sc=new Scanner(System.in);构造函数源码如下

1
2
3
public Scanner(InputStream source) {
this(new InputStreamReader(source), WHITESPACE_PATTERN);
}

可看出是调用了另外一个构造函数,继续查看源码

1
2
3
4
5
6
7
8
9
10
11
12
private Scanner(Readable source, Pattern pattern) {
assert source != null : "source should not be null";
assert pattern != null : "pattern should not be null";
this.source = source; //看这句
delimPattern = pattern;
buf = CharBuffer.allocate(BUFFER_SIZE);
buf.limit(0);
matcher = delimPattern.matcher(buf);
matcher.useTransparentBounds(true);
matcher.useAnchoringBounds(false);
useLocale(Locale.getDefault(Locale.Category.FORMAT));
}

至少知道了Scanner内部还是用到了流,算是对流进行了封装吧,使用起来更方便一些

close()

调用语句为sc.close();,查看close()源码,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void close() {
if (closed) //1.通过closed标志校验Scanner是否已关闭;
return;
if (source instanceof Closeable) { //2.执行source的close()方法,
try { //将source关闭(这里为System.in);
((Closeable)source).close();
} catch (IOException ioe) {
lastException = ioe;
}
}
sourceClosed = true; //3.将sourceClosed标志设置为true,表示source已关闭;
source = null; //4.将source置为null,不再引用,处于可回收状态;
closed = true; //5.将closed标志设置为true,表示Scanner已关闭;
}

可以知道关闭Scanner的时候,((Closeable)source).close();System.in关闭了

关闭后下次想再使用就当然有错了~(除非构造函数里还再把in给打开,但这样也不太合理)

其实如果还可以再仔细看看Scanner的构造方法,和Scannernext(),能力有限,点到为止

作者:@臭咸鱼

本文为作者原创,转载请注明出处:https://chouxianyu.github.io/2018/11/03/java.util.NoSuchElementException/#more

欢迎转发和评论