简体   繁体   English

java.util.NoSuchElementException - 扫描仪读取用户输入

[英]java.util.NoSuchElementException - Scanner reading user input

I'm new to using Java, but I have some previous experience with C#. The issue I'm having comes with reading user input from console.我是 Java 的新手,但我以前有过 C# 的一些经验。我遇到的问题是从控制台读取用户输入。

I'm running into the "java.util.NoSuchElementException" error with this portion of code:我遇到了这部分代码的“java.util.NoSuchElementException”错误:

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

I have two functions that get user input:我有两个获取用户输入的函数:

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

If I don't call PromptCustomerQty then I don't get this error, which leads me to believe I am doing something wrong with scanner.如果我不调用 PromptCustomerQty 那么我就不会收到此错误,这让我相信我在使用扫描仪时做错了什么。 Below is my full code sample.下面是我的完整代码示例。 I appreciate any help.我感谢任何帮助。

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(); 
}

This has really puzzled me for a while but this is what I found in the end.这真的让我困惑了一段时间,但这是我最终发现的。

When you call, sc.close() in first method, it not only closes your scanner but closes your System.in input stream as well.当您在第一个方法中调用sc.close()时,它不仅会关闭您的扫描仪,还会关闭您的System.in输入流。 You can verify it by printing its status at very top of the second method as :您可以通过在第二种方法的最顶部打印其状态来验证它:

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

So, now when you re-instantiate, Scanner in second method, it doesn't find any open System.in stream and hence the exception.因此,现在当您在第二种方法中重新实例化Scanner时,它找不到任何打开的System.in流,因此找不到异常。

I doubt if there is any way out to reopen System.in because:我怀疑是否有任何方法可以重新打开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.**

The only good solution for your problem is to initiate the Scanner in your main method, pass that as argument in your two methods, and close it again in your main method eg:解决您的问题的唯一好方法是在您的 main 方法中启动Scanner ,在您的两个方法中将其作为参数传递,然后在您的 main 方法中再次关闭它,例如:

main method related code block: 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();

Your Methods:你的方法:

 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
 }

Hope this gives you some insight about the failure and possible resolution.希望这能让您对失败和可能的解决方案有所了解。

The problem is问题是

When a Scanner is closed, it will close its input source if the source implements the Closeable interface.当 Scanner 关闭时,如果源实现了 Closeable 接口,它将关闭其输入源。

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

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

To fix it you can make要修复它,您可以制作

Scanner scan static and do not close it in PromptCustomerQty. Scanner scan static并且不要在 PromptCustomerQty 中关闭它。 Code below works.下面的代码有效。

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);
}
}

On a side note, you shouldn't use == for String comparision, use .equals instead.附带说明一下,您不应该使用==进行字符串比较,而是使用.equals

You need to remove the scanner closing lines: scan.close();您需要删除扫描仪关闭行: scan.close();

It happened to me before and that was the reason.它以前发生在我身上,这就是原因。

the reason of the exception has been explained already, however the suggested solution isn't really the best.已经解释了异常的原因,但是建议的解决方案并不是最好的。

You should create a class that keeps a Scanner as private using Singleton Pattern, that makes that scanner unique on your code.您应该使用单例模式创建一个将 Scanner 保持为私有的类,这使得该扫描仪在您的代码中是唯一的。

Then you can implement the methods you need or you can create a getScanner ( not recommended ) and you can control it with a private boolean, something like alreadyClosed.然后你可以实现你需要的方法,或者你可以创建一个 getScanner(不推荐),你可以用一个私有的布尔值来控制它,比如已经关闭。

If you are not aware how to use Singleton Pattern, here's a example:如果你不知道如何使用单例模式,这里有一个例子:

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();
    }   
}

For anyone who arrived here while taking an online exam on a site like HackerRank-对于在 HackerRank 等网站上参加在线考试时到达这里的任何人-

You might receive this if you are trying to test your (possibly) perfectly fine code by clicking the button to execute main() with custom input.如果您尝试通过单击按钮以使用自定义输入执行 main() 来测试您的(可能)完美的代码,您可能会收到此信息。

In this case, you need to be clicking the other button, something like "Run Unit Tests".在这种情况下,您需要单击另一个按钮,例如“运行单元测试”。 You're likely only being evaluated on whether the code passes the unit tests they wrote- not your ability to refactor code to fewer LOC or your coding style.您可能只会被评估代码是否通过了他们编写的单元测试 - 而不是您将代码重构为更少 LOC 的能力或您的编码风格。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM