java.util.NoSuchElementException – Entrada de usuario de lectura del escáner

Soy nuevo en el uso de Java, pero tengo experiencia previa con C #. El problema que tengo viene con leer la entrada del usuario desde la consola.

Me estoy encontrando con el error “java.util.NoSuchElementException” con esta porción de código:

payment = sc.next(); // PromptCustomerPayment function 

Tengo dos funciones que reciben la entrada del usuario:

  • PromptCustomerQty
  • PromptCustomerPayment

Si no llamo a PromptCustomerQty, entonces no obtengo este error, lo que me lleva a pensar que estoy haciendo algo mal con el escáner. A continuación está mi muestra de código completo. Agradezco cualquier ayuda.

 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 ProductList = new ArrayList(); // 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 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  "); payment = sc.next(); } while ((!payment.toLowerCase().equals("yes")) && (!payment.toLowerCase().equals("no"))); // Check/set result if (payment.toLowerCase() == "yes") { customer.setPaidInFull(true); } else { customer.setPaidInFull(false); } // Cleanup sc.close(); } 

Esto realmente me ha intrigado por un tiempo, pero esto es lo que encontré al final.

Cuando llama, sc.close() en el primer método, no solo cierra su escáner sino que también cierra su flujo de entrada System.in . Puede verificarlo imprimiendo su estado en la parte superior del segundo método como:

  System.out.println(System.in.available()); 

Entonces, ahora cuando vuelve a crear instancias, Scanner en el segundo método, no encuentra ninguna secuencia abierta de System.in y, por lo tanto, la excepción.

Dudo si hay alguna forma de volver a abrir System.in porque:

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.**

La única buena solución para su problema es iniciar el Scanner en su método principal, pasarlo como argumento en sus dos métodos, y cerrarlo nuevamente en su método principal, por ejemplo:

bloque de código relacionado con el método 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(); 

Tus métodos:

  public static void PromptCustomerQty(Customer customer, ArrayList 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 } 

Espero que esto te dé una idea sobre el fracaso y la posible resolución.

El problema es

Cuando un Escáner está cerrado, cerrará su fuente de entrada si la fuente implementa la interfaz de Cierre.

http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Scanner.html

Así, scan.close() cierra System.in .

Para arreglarlo puedes hacer

Scanner scan static y no lo cierra en PromptCustomerQty. El código a continuación funciona.

 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 ProductList = new ArrayList(); // 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 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); } } 

En una nota lateral, no debe usar == para la comparación de cadenas, use .equals en .equals lugar.

Después de esta línea:

 qty = scan.nextInt(); 

Siempre agregue una línea más para borrar el escáner:

 scan.nextLine(); 

Además, usa

 sc.nextLine(); 

en lugar de

 sc.next();