【问题标题】:java.util.NoSuchElementException - Scanner reading user inputjava.util.NoSuchElementException - 扫描器读取用户输入
【发布时间】:2012-10-24 02:04:45
【问题描述】:

我是使用 Java 的新手,但我以前使用过 C#。我遇到的问题是从控制台读取用户输入。

我在这部分代码中遇到了“java.util.NoSuchElementException”错误:

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

我有两个获取用户输入的函数:

  • 提示客户数量
  • 提示客户付款

如果我不调用 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(); 
}

【问题讨论】:

    标签: java input java.util.scanner


    【解决方案1】:

    这确实让我困惑了一段时间,但这是我最终发现的。

    当您在第一种方法中调用sc.close() 时,它不仅会关闭您的扫描仪,还会关闭您的System.in 输入流。您可以通过在第二种方法的最顶部打印其状态来验证它:

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

    所以,现在当您在第二种方法中重新实例化 Scanner 时,它找不到任何打开的 System.in 流,因此出现异常。

    我怀疑是否有办法重新打开System.in,因为:

    public void close() throws IOException --&gt; 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.**

    解决您的问题的唯一好方法是在您的 main 方法中启动 Scanner,将其作为参数传递给您的两个方法,然后在您的 main 方法中再次关闭它,例如:

    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
     }
    

    希望这能让您对失败和可能的解决方案有所了解。

    【讨论】:

    • 感谢您的回复。您对幕后实际发生的事情的解释非常有帮助
    • 感谢您的回复。这是真的!
    • 天哪,我被困了这么久才找到这篇文章。非常感谢!你太棒了。
    • 虽然关闭扫描仪是一种很好的做法,但为了避免此错误,最好不要关闭扫描仪。它避免了所有这些错误。我们总是可以抑制警告
    • 我想知道这是否是一个误导性异常的情况。 IE。封闭的输入流有一个明确的条件,但行为是如果它没有任何问题, - 只是没有这样的元素......让我想起了由 NullPointerException 以更温和的形式引起的运行时混乱。
    【解决方案2】:

    问题是

    当 Scanner 关闭时,如果源实现了 Closeable 接口,它将关闭其输入源。

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

    因此scan.close() 关闭System.in

    要修复它,您可以制作

    Scanner scanstatic 并且不要在 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

    【讨论】:

    • 但是他们的方法不同吧?它们的创建和关闭位置。
    • 在原始代码中它们是。在固定代码中,我对这两种方法都使用了一个 Scanner 实例。
    • 我仍然很困惑为什么第二种方法中的sc.next() 甚至sc.nextLine() 会导致异常。
    • @YogendraSingh:nextLine() 是否也抛出相同的异常?
    • @BheshGurung:对我来说,它说java.util.NoSuchElementException: No line found
    【解决方案3】:

    您需要删除扫描仪结束行:scan.close();

    我以前也遇到过,这就是原因。

    【讨论】:

      【解决方案4】:

      已经解释了异常的原因,但是建议的解决方案并不是最好的。

      您应该创建一个使用单例模式将 Scanner 保持为私有的类,这使得该扫描仪在您的代码中是唯一的。

      然后你可以实现你需要的方法,或者你可以创建一个 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();
          }   
      }
      
      

      【讨论】:

        【解决方案5】:

        对于在 HackerRank 等网站上参加在线考试时到达这里的任何人-

        如果您尝试通过单击按钮以使用自定义输入执行 main() 来测试(可能)完美的代码,您可能会收到此消息。

        在这种情况下,您需要单击另一个按钮,例如“运行单元测试”。您可能只会被评估代码是否通过了他们编写的单元测试 - 而不是您将代码重构为更少 LOC 的能力或您的编码风格。

        【讨论】:

          猜你喜欢
          • 2022-01-07
          • 1970-01-01
          • 2012-07-21
          • 1970-01-01
          相关资源
          最近更新 更多