java.util.NoSuchElementException在switch语句中读取用户输入时发生错误。

huangapple go评论78阅读模式
英文:

java.util.NoSuchElementException when reading user input in switch statement

问题

以下是翻译好的部分:

在我正在进行的计算机科学课程的项目中,遇到了一个错误,希望有人能够为我解释可能发生的情况。下面是代码中发生的情况的一些背景信息:

在下面的代码中,我有程序的开头部分,显示一个 UI 菜单并询问用户输入,然后使用 switch 语句根据用户输入运行适当的方法。

	/**
	 * 程序的开始
	 */
	public static void main(String[] args) {

		Library library = new Library();
		
		char userChoice; // 存储用户选择的菜单项
		Scanner userMenuChoiceScanner = new Scanner(System.in);
		boolean loop = true;

		displayMenu(); // 显示主 UI 菜单
		userChoice = Character.toLowerCase(userMenuChoiceScanner.nextLine().charAt(0)); // 获取用户的菜单选择
		
		/* 在用户做出菜单选择后循环 UI 菜单,并在用户从菜单中选择 'q' 时退出循环。*/
		while(loop) {

			/* switch 判断用户选择的菜单项,以及是否重新打印菜单 UI,加载、搜索、打印或分析音乐目录,或退出程序。*/
			switch(userChoice) {
			case 'm':
				System.out.println();
				displayMenu();
				userChoice = Character.toLowerCase(userMenuChoiceScanner.nextLine().charAt(0));
				break;
			case 'p':
				System.out.println();
				
				if(library.getBooks().size() != 0) {
					printLibrary(library);
				} else {
					System.out.println("图书馆中没有书籍。请先添加一本书。");
				}
				
				System.out.print("请输入命令(按 'm' 查看菜单):");
				userChoice = Character.toLowerCase(userMenuChoiceScanner.nextLine().charAt(0));
				break;
			case 'a':
				System.out.println();
				addBookToLibrary(library);
				System.out.print("请输入命令(按 'm' 查看菜单):");
				userChoice = Character.toLowerCase(userMenuChoiceScanner.nextLine().charAt(0));
				break;
			case 'd':
				
				System.out.println();
				
				if(library.getBooks().size() != 0) {
					deleteBookFromLibrary(library);
				} else {
					System.out.println("图书馆中没有书籍。请先添加一本书。");
				}

				System.out.print("请输入命令(按 'm' 查看菜单):");
				userChoice = Character.toLowerCase(userMenuChoiceScanner.nextLine().charAt(0));
				break;
			case 'r':			
				System.out.println();
				
				if(library.getBooks().size() != 0) {
					readBookFromLibrary(library);
				} else {
					System.out.println("图书馆中没有书籍。请先添加一本书。");
				}
				
				System.out.print("请输入命令(按 'm' 查看菜单):");
				userChoice = Character.toLowerCase(userMenuChoiceScanner.nextLine().charAt(0));
				break;
			case 'q':
				System.out.println();
				System.out.println("谢谢!再见!");
				loop = false;
				break;
			default:
				System.out.println();
				System.out.println("无效的选择!");
				System.out.print("请输入命令(按 'm' 查看菜单):");
				userChoice = Character.toLowerCase(userMenuChoiceScanner.nextLine().charAt(0));
				System.out.println();

			}
		}

		userMenuChoiceScanner.close();
	}

发生的情况是,当用户做出选择后,它进入适当的方法并在那里执行任务,然后当它返回到 switch 语句以获取新的用户输入时,它抛出一个 java.util.NoSuchElementException 错误,就好像扫描器流已关闭,但我直到最后才关闭扫描器(流应该仍然是打开的)。

switch 语句的设置是这样的,必须先添加书籍(用户必须首先选择 'a'),然后才能选择任何其他选项。以下是 addBookToLibrary() 方法的代码,其中有几个打开和关闭的扫描器。我假设关闭这些扫描器可能会导致问题?

	/**
	 * 向图书馆添加新书
	 * @param library 包含书籍对象的 ArrayList 对象
	 */
	public static void addBookToLibrary(Library library) {
		Scanner addBookScanner = new Scanner(System.in);
		String title = "";
		String author = "";
		String genre = "";
		String filename = "";
		boolean addStatus = false;
		
		
		/* 请求用户输入书籍详细信息 */
		System.out.println("输入要添加到图书馆的书籍详细信息:");
		System.out.println("书名是什么?");
		title = addBookScanner.nextLine();
		System.out.println("作者的名字是什么?");
		author = addBookScanner.nextLine();
		System.out.println("书籍类型是什么?");
		genre = addBookScanner.nextLine();
		System.out.println("书籍文件名是什么?");
		filename = addBookScanner.nextLine();
		addBookScanner.close();
		
		Book newBook = new Book(author, title); // 使用作者和标题创建新书
		
		newBook.setGenre(genre); // 设置书籍类型

		File bookFile = new File(System.getProperty("user.dir") + '\\' + filename); // 用于检查用户提供的文件是否存在
		System.out.println(bookFile);
		/* 检查用户提供的文件是否实际存在 */
		if(bookFile.exists()) {
			
			try {
				
				newBook.setFilename(filename);
				
			}catch(Exception e) {
				
			}
			
		}
		else {
			
			Scanner getNewFilenameScanner = new Scanner(System.in);
			
			// 如果文件不存在,继续要求用户输入新的文件名
			do {
			System.out.println("抱歉,但您指定的文件不存在。");
			System.out.print("输入新的文件名:");
			bookFile = new File(getNewFilenameScanner.nextLine());
			
			}while (!(bookFile.exists()));
			
			getNewFilenameScanner.close();
		}
		
		addStatus = library.addBook(newBook); // 将书籍添加到图书馆
		
		if(addStatus) {
			System.out.println("成功

<details>
<summary>英文:</summary>

Running into an error with this project I&#39;m working on for a computer science course, and hoping someone can give me insight into what may be happening. A bit of background to what&#39;s occurring in the code:

In the below code, I have the start of my program which displays a UI menu and asks for user input and uses a switch statement to run appropriate methods based on user input. 
/**
* Start of program
*/
public static void main(String[] args) {
Library library = new Library();
char userChoice; //Stores what menu item the user chose
Scanner userMenuChoiceScanner = new Scanner(System.in);
boolean loop = true;
displayMenu(); //Displays main UI menu
userChoice = Character.toLowerCase(userMenuChoiceScanner.nextLine().charAt(0)); //Gets user&#39;s menu choice
/*Loops UI menu afer user has made menu choice and exits loop if user choses &#39;q&#39; from menu.*/
while(loop) {
/* Switch descides what menu choice user has made and whether to prin menu UI again, load, 
* search, print, or analyze a music catalog, or to quit program.
*/
switch(userChoice) {
case &#39;m&#39;:
System.out.println();
displayMenu();
userChoice = Character.toLowerCase(userMenuChoiceScanner.nextLine().charAt(0));
break;
case &#39;p&#39;:
System.out.println();
if(library.getBooks().size() != 0) {
printLibrary(library);
} else {
System.out.println(&quot;There are no books in the library. Please add a book first.&quot;);
}
System.out.print(&quot;Please enter a command (press &#39;m&#39; for Menu):&quot;);
userChoice = Character.toLowerCase(userMenuChoiceScanner.nextLine().charAt(0));
break;
case &#39;a&#39;:
System.out.println();
addBookToLibrary(library);
System.out.print(&quot;Please enter a command (press &#39;m&#39; for Menu):&quot;);
userChoice = Character.toLowerCase(userMenuChoiceScanner.nextLine().charAt(0));
break;
case &#39;d&#39;:
System.out.println();
if(library.getBooks().size() != 0) {
deleteBookFromLibrary(library);
} else {
System.out.println(&quot;There are no books in the library. Please add a book first.&quot;);
}
System.out.print(&quot;Please enter a command (press &#39;m&#39; for Menu):&quot;);
userChoice = Character.toLowerCase(userMenuChoiceScanner.nextLine().charAt(0));
break;
case &#39;r&#39;:			
System.out.println();
if(library.getBooks().size() != 0) {
readBookFromLibrary(library);
} else {
System.out.println(&quot;There are no books in the library. Please add a book first.&quot;);
}
System.out.print(&quot;Please enter a command (press &#39;m&#39; for Menu):&quot;);
userChoice = Character.toLowerCase(userMenuChoiceScanner.nextLine().charAt(0));
break;
case &#39;q&#39;:
System.out.println();
System.out.println(&quot;Thank you! Goodbye!&quot;);
loop = false;
break;
default:
System.out.println();
System.out.println(&quot;Invalid selection!&quot;);
System.out.print(&quot;Please enter a command (press &#39;m&#39; for Menu):&quot;);
userChoice = Character.toLowerCase(userMenuChoiceScanner.nextLine().charAt(0));
System.out.println();
}
}
userMenuChoiceScanner.close();
}

What&#39;s occuring is when tthe user makes a choice, it goes into the appropriate methods and fulfills it&#39;s tasks there, then when it returns to the switch statement to get a new user input, it throws a `java.util.NoSuchElementException` error as if the scanner stream was closed, but I don&#39;t close the scanner until the end (the stream should still be open). 
The switch statement is set up in a way where a book must be added (the user must chose &#39;a&#39; first) before any other option can be chosen. Here is the code to he `addBookToLibrary()` method which has a couple scanners which are opened and closed. I assume maybe closing out these scanners may be causing the issue?
/**
* Adds new book to library
* @param library ArrayList object which holds book objects
*/
public static void addBookToLibrary(Library library) {
Scanner addBookScanner = new Scanner(System.in);
String title = &quot;&quot;;
String author = &quot;&quot;;
String genre = &quot;&quot;;
String filename = &quot;&quot;;
boolean addStatus = false;
/*Asks user for book details */
System.out.println(&quot;Enter the details of the book you want to add to the library:&quot;);
System.out.println(&quot;What is the book title?&quot;);
title = addBookScanner.nextLine();
System.out.println(&quot;What is the author&#39;s name?&quot;);
author = addBookScanner.nextLine();
System.out.println(&quot;What is book genre?&quot;);
genre = addBookScanner.nextLine();
System.out.println(&quot;What is the book&#39;s filename?&quot;);
filename = addBookScanner.nextLine();
addBookScanner.close();
Book newBook = new Book(author, title); //creates new book with author and title set
newBook.setGenre(genre); //sets book genre
File bookFile = new File(System.getProperty(&quot;user.dir&quot;) + &#39;\\&#39; + filename); //used to check if file user provided exists
System.out.println(bookFile);
/*Checks to see if file user provided actually exists*/
if(bookFile.exists()) {
try {
newBook.setFilename(filename);
}catch(Exception e) {
}
}
else {
Scanner getNewFilenameScanner = new Scanner(System.in);
//Continues to ask user for new filename if file doesn&#39;t exist
do {
System.out.println(&quot;I&#39;m sorry, but the file you specified does not exist.&quot;);
System.out.print(&quot;Enter a new file name:&quot;);
bookFile = new File(getNewFilenameScanner.nextLine());
}while (!(bookFile.exists()));
getNewFilenameScanner.close();
}
addStatus = library.addBook(newBook); //adds book to library
if(addStatus) {
System.out.println(&quot;Book was successfully added!&quot;);
}
else {
System.out.println(&quot;Book was not successfully added. Please try again.&quot;);
}
}

This code worked perfectly in a previous project, so I&#39;m not certain why it&#39;s not working now. Any help would be greatly appreciated.
Thank you so much
</details>
# 答案1
**得分**: 1
在你的`addBookToLibrary`方法中,你在使用完`Scanner`后进行了`close`操作。然而,这些`Scanner`与`System.in`即标准输入流相连接。根据[文档](https://docs.oracle.com/javase/8/docs/api/java/util/Scanner.html#close--),关闭这些`Scanner`也会关闭标准输入流。
还有什么连接到标准输入流呢?`userMenuChoiceScanner`!当标准输入流关闭后,`userMenuChoiceScanner`无法从中读取,从而引发异常。
需要注意的是,尽管`userMenuChoiceScanner`直到最后才关闭,但它所读取的流已经关闭了。
事实上,你不需要在这里创建多个`Scanner`。你只需要使用一个`Scanner`,并将其传递给不同的方法。例如,`addBookToLibrary`可以接受一个`Scanner`:
```java
public static void addBookToLibrary(Library library, Scanner s) {

然后它只会使用S来读取输入。你可以将你的userMenuChoiceScanner传递给它:

addBookToLibrary(library, userMenuChoiceScanner);

作为一般规则,你不应该关闭任何不是由你打开的东西。标准输入流不是你打开的(是JVM打开的),所以你不应该关闭它。

英文:

In your addBookToLibrary method, you close the Scanners after you are done using them. However, those scanners are connected to System.in, the standard input stream. Closing those scanners will also close the standard input stream, according to the documentation.

What else is connected to the standard input stream? userMenuChoiceScanner! After the standard input stream is closed, userMenuChoiceScanner can't read from it, hence throwing an exception.

Note that although userMenuChoiceScanner is not closed until the very end, the stream that it is reading from is closed.

In fact, you don't need to create multiple scanners here. You only need to use one scanner, and pass it around to different methods. For example, addBookToLibrary could accept a Scanner:

public static void addBookToLibrary(Library library, Scanner s) {

And it will only use s to read inputs. You can pass your userMenuChoiceScanner to it:

addBookToLibrary(library, userMenuChoiceScanner);

As a general rule, you should not close anything that is not opened by you. You didn't open the standard input stream (the JVM did), so you shouldn't close it.

huangapple
  • 本文由 发表于 2020年4月5日 18:34:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/61041251.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定