How to make a ONE static scanner global variable w

2019-06-14 18:38发布

问题:

I want to create a static scanner but i will like to put the try catch block around it so it can automatically close avoiding resources leaks and or this exception:

Exception in thread "main" java.util.NoSuchElementException: No line found
    at java.util.Scanner.nextLine(Scanner.java:1585)
    at softwareEngineer.UserApp1.main(UserApp1.java:82)

Essentially I only want to create one static scanner declaration and use it throughout the main program and includes the static methods, at this point my code will require to create separate scanner for each method and you are force "scan.close()". the code below will recieve a exception handling error due to multiple scanner that was open and did not closein the program.

I updated the code now i get null pointer exception

import java.util.Scanner;

public class UserApp1 {
    static User currentCustomer = null; //single object
    static Scanner scan;
    //------------------------------------------------------- 
    // Create a list, then repeatedly print the menu and do what the 
    // user asks until they quit 
    //------------------------------------------------------- 
    public static void main(String[] args) {

     scan = new Scanner(System.in);)//scanner to avoid resource leak
            printMenu(); //print menu system from another function
            String choice = scan.nextLine(); //reads an input
            final String EXIT_now = "0";
            final String BACK = "back";
               while (!(choice.equalsIgnoreCase(EXIT_now))){

          switch(choice) {
                 case 1: break;
                             case 2: 
                               currentCustomer = loginInput();<---- errors happens here
                if(currentCustomer != null){
                    System.out.println("You have successfully login");
                }
                break;
                default: 
                    System.out.println("Sorry, invalid choice"); 
                    break;
                } //ends switch  
                printMenu(); //print menu system from another function
                choice = scan.nextLine(); //reads an input
            }//ends while 

    System.out.println("\t\t GoodBye!\n Thank you for trying our program.");           
        System.exit(0);
    }//ends main

    //---------------------------- 
    // Print the user's choices 
    //---------------------------- 
    public static void printMenu() { 
        System.out.println("\t\t The User Login System "); 
        System.out.println("\t\t ======================"); 
        System.out.println("The Menu Options:"); 
        System.out.println("1: Register an Account");
        System.out.println("2: Login to your Account");
        System.out.println("3: Reset Password");
        System.out.println("0: Quit/Exit ");

        System.out.println("Please enter your selection > "); 
    } //ends printMenu 

      public static User loginInput(){

         System.out.print( "\nFollow the Prompts to Log-In to your Account  \n ");
            System.out.print( "\nPlease enter your userid : \n ");
            String userid = scan.nextLine();// <---- errors happens here

            System.out.print( "\nPlease enter your password: \n ");
            String pass = scan.nextLine();

            currentCustomer = AccountList.loginUser(userid, pass);

            if (currentCustomer != null)
            {    
                return currentCustomer;
            }
            return null;
    }//ends loginInput

}//ends class*

回答1:

You're using a try-with-resources, which will automatically close it when you finish the try block. Try setting it to a variable like so:

public class MyClass {

    private static Scanner scan;

    public static void main(String[] args) {

        scan = new Scanner(System.in);

    }

}

Avoid making multiple scanners with the System.in input as well, as they will consume the stream and then you have an entirely different problem.



回答2:

Avoid using a static global Scanner at all, by passing the Scanner instance you want to work with to the relevant methods. Consider this simplified example:

public static void main(String[] args) {
  try(Scanner in = new Scanner(System.in)) {
    String choice = in.nextLine().trim();
    if(choice.equals("1")) {
      doOp1(in);
    } else if(choice.equals("2")) {
      doOp2(in);
    } else {
      System.err.println("Invalid choice.  Goodbye.");
    }
  }
}

// Method takes an open, functioning Scanner as an argument, therefore
// it doesn't need to close it, or worry about where it came from, it
// simply uses it, does what it needs to do, and returns, trusting
// the caller to properly close the Scanner, since it opened it.
private void doOp1(Scanner in) {
  System.out.print("What is your name? ");
  String name = in.nextLine().trim();
  System.out.print("What is your favorite color? ");
  String color = in.nextLine().trim();
}

private void doOpt2(Scanner in) {
  ...
}

You want to compartmentalize your resources to ensure they are limited in scope and easy to close. Putting them in global state of any kind makes that very difficult. Instead, separate the opening and closing of the resource from the code using it. This sort of compartmentalization makes for much more maintainable, readable, and testable code.

For instance, by passing an already open Scanner to your core business logic functions, you can mock a real user's behavior and create a test to ensure your code remains stable, by constructing a Scanner that reads from a hard coded String, and passing that into your method, without needing to run the whole class and type in the behavior your testing manually again and again.