java.util.NoSuchElementException - Сканер, считывающий ввод пользователя
Я новичок в использовании Java, но у меня есть некоторый предыдущий опыт работы с C #. Проблема, с которой я сталкиваюсь, связана с чтением пользовательского ввода с консоли.
Я сталкиваюсь с ошибкой "java.util.NoSuchElementException" в этой части кода:
payment = sc.next(); // PromptCustomerPayment function
У меня есть две функции, которые получают пользовательский ввод:
- PromptCustomerQty
- PromptCustomerPayment
Если я не вызываю PromptCustomerQty, я не получаю эту ошибку, что наводит меня на мысль, что я делаю что-то не так со сканером. Ниже приведен мой полный пример кода. Я ценю любую помощь.
public static void main (String[] args) {
// Create a customer
// Future proofing the possabiltiies of multiple customers
Customer customer = new Customer("Will");
// Create object for each Product
// (Name,Code,Description,Price)
// Initalize Qty at 0
Product Computer = new Product("Computer","PC1003","Basic Computer",399.99);
Product Monitor = new Product("Monitor","MN1003","LCD Monitor",99.99);
Product Printer = new Product("Printer","PR1003x","Inkjet Printer",54.23);
// Define internal variables
// ## DONT CHANGE
ArrayList<Product> ProductList = new ArrayList<Product>(); // List to store Products
String formatString = "%-15s %-10s %-20s %-10s %-10s %n"; // Default format for output
// Add objects to list
ProductList.add(Computer);
ProductList.add(Monitor);
ProductList.add(Printer);
// Ask users for quantities
PromptCustomerQty(customer, ProductList);
// Ask user for payment method
PromptCustomerPayment(customer);
// Create the header
PrintHeader(customer, formatString);
// Create Body
PrintBody(ProductList, formatString);
}
public static void PromptCustomerQty(Customer customer, ArrayList<Product> ProductList) {
// Initiate a Scanner
Scanner scan = new Scanner(System.in);
// **** VARIABLES ****
int qty = 0;
// Greet Customer
System.out.println("Hello " + customer.getName());
// Loop through each item and ask for qty desired
for (Product p : ProductList) {
do {
// Ask user for qty
System.out.println("How many would you like for product: " + p.name);
System.out.print("> ");
// Get input and set qty for the object
qty = scan.nextInt();
}
while (qty < 0); // Validation
p.setQty(qty); // Set qty for object
qty = 0; // Reset count
}
// Cleanup
scan.close();
}
public static void PromptCustomerPayment (Customer customer) {
// Initiate Scanner
Scanner sc = new Scanner(System.in);
// Variables
String payment = "";
// Prompt User
do {
System.out.println("Would you like to pay in full? [Yes/No]");
System.out.print("> ");
payment = sc.next();
} while ((!payment.toLowerCase().equals("yes")) && (!payment.toLowerCase().equals("no")));
// Check/set result
if (payment.toLowerCase().equals("yes")) {
customer.setPaidInFull(true);
}
else {
customer.setPaidInFull(false);
}
// Cleanup
sc.close();
}
Переведено автоматически
Ответ 1
Это действительно озадачивало меня некоторое время, но это то, что я нашел в конце.
При вызове sc.close()
первого метода он не только закрывает ваш сканер, но и закрывает ваш System.in
поток ввода. Вы можете проверить это, напечатав его состояние в самом верху второго метода как :
System.out.println(System.in.available());
Итак, теперь, когда вы повторно создаете экземпляр, Scanner
во втором методе он не находит никакого открытого System.in
потока и, следовательно, исключения.
Я сомневаюсь, что есть какой-либо выход для повторного открытия System.in
потому что:
public void close() throws IOException --> Closes this input stream and releases any system resources associated with this stream. The general contract of close is that it closes the input stream. A closed stream cannot perform input operations and **cannot be reopened.**
Единственное хорошее решение вашей проблемы - инициировать Scanner
в вашем основном методе, передать это в качестве аргумента в ваших двух методах и снова закрыть его в вашем основном методе, например:
main
блок кода, связанный с методом:
Scanner scanner = new Scanner(System.in);
// Ask users for quantities
PromptCustomerQty(customer, ProductList, scanner );
// Ask user for payment method
PromptCustomerPayment(customer, scanner );
//close the scanner
scanner.close();
Ваши методы:
public static void PromptCustomerQty(Customer customer,
ArrayList<Product> ProductList, Scanner scanner) {
// no more scanner instantiation
...
// no more scanner close
}
public static void PromptCustomerPayment (Customer customer, Scanner sc) {
// no more scanner instantiation
...
// no more scanner close
}
Надеюсь, это даст вам некоторое представление о сбое и возможном разрешении.
Ответ 2
Проблема в том, что
Когда сканер закрыт, он закроет свой источник ввода, если источник реализует интерфейс Closeable.
http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Scanner.html
Таким образом, scan.close()
закрывается System.in
.
Чтобы исправить это, вы можете сделать
Scanner scan
static
и не закрывайте его в PromptCustomerQty . Приведенный ниже код работает.
public static void main (String[] args) {
// Create a customer
// Future proofing the possabiltiies of multiple customers
Customer customer = new Customer("Will");
// Create object for each Product
// (Name,Code,Description,Price)
// Initalize Qty at 0
Product Computer = new Product("Computer","PC1003","Basic Computer",399.99);
Product Monitor = new Product("Monitor","MN1003","LCD Monitor",99.99);
Product Printer = new Product("Printer","PR1003x","Inkjet Printer",54.23);
// Define internal variables
// ## DONT CHANGE
ArrayList<Product> ProductList = new ArrayList<Product>(); // List to store Products
String formatString = "%-15s %-10s %-20s %-10s %-10s %n"; // Default format for output
// Add objects to list
ProductList.add(Computer);
ProductList.add(Monitor);
ProductList.add(Printer);
// Ask users for quantities
PromptCustomerQty(customer, ProductList);
// Ask user for payment method
PromptCustomerPayment(customer);
// Create the header
PrintHeader(customer, formatString);
// Create Body
PrintBody(ProductList, formatString);
}
static Scanner scan;
public static void PromptCustomerQty(Customer customer, ArrayList<Product> ProductList) {
// Initiate a Scanner
scan = new Scanner(System.in);
// **** VARIABLES ****
int qty = 0;
// Greet Customer
System.out.println("Hello " + customer.getName());
// Loop through each item and ask for qty desired
for (Product p : ProductList) {
do {
// Ask user for qty
System.out.println("How many would you like for product: " + p.name);
System.out.print("> ");
// Get input and set qty for the object
qty = scan.nextInt();
}
while (qty < 0); // Validation
p.setQty(qty); // Set qty for object
qty = 0; // Reset count
}
// Cleanup
}
public static void PromptCustomerPayment (Customer customer) {
// Variables
String payment = "";
// Prompt User
do {
System.out.println("Would you like to pay in full? [Yes/No]");
System.out.print("> ");
payment = scan.next();
} while ((!payment.toLowerCase().equals("yes")) && (!payment.toLowerCase().equals("no")));
// Check/set result
if (payment.toLowerCase() == "yes") {
customer.setPaidInFull(true);
}
else {
customer.setPaidInFull(false);
}
}
Кстати, вам не следует использовать ==
для сравнения строк, используйте .equals
вместо этого.
Ответ 3
Вам необходимо удалить строки, закрывающие сканер: scan.close();
Это случалось со мной раньше, и это было причиной.
Ответ 4
причина исключения уже была объяснена, однако предлагаемое решение на самом деле не самое лучшее.
Вы должны создать класс, который сохраняет сканер как закрытый, используя шаблон Singleton, который делает этот сканер уникальным в вашем коде.
Затем вы можете реализовать нужные вам методы или создать getScanner ( не рекомендуется ) и управлять им с помощью частного логического значения, что-то вроде alreadyClosed .
Если вы не знаете, как использовать одноэлементный шаблон, вот пример:
public class Reader {
private Scanner reader;
private static Reader singleton = null;
private boolean alreadyClosed;
private Reader() {
alreadyClosed = false;
reader = new Scanner(System.in);
}
public static Reader getInstance() {
if(singleton == null) {
singleton = new Reader();
}
return singleton;
}
public int nextInt() throws AlreadyClosedException {
if(!alreadyClosed) {
return reader.nextInt();
}
throw new AlreadyClosedException(); //Custom exception
}
public double nextDouble() throws AlreadyClosedException {
if(!alreadyClosed) {
return reader.nextDouble();
}
throw new AlreadyClosedException();
}
public String nextLine() throws AlreadyClosedException {
if(!alreadyClosed) {
return reader.nextLine();
}
throw new AlreadyClosedException();
}
public void close() {
alreadyClosed = true;
reader.close();
}
}