Kuby Block

Blog about programming, tech, society and more.

Be careful when using Scanner in Java

poster

To interact with a java application in command line, Scanner is one of the most popular way to implement.

But you know, it also has a mistake that we may easily make.

First, let use it in a simple program:


import java.util.Scanner;

public class Main {

  private static String inputString() {
    final Scanner scanner = new Scanner(System.in);
    String result = "";
    if (scanner.hasNextLine()) {
      result = scanner.nextLine();
    }
    return result;
  }

  public static void main(String[] args) {
    System.out.println("input fullName: ");
    final String value = inputString();
    System.out.println("fullName: " + value);
  }
}

/** Output:

input fullName:
John
fullName: hello

**/

Everything works well, yeah!

But, if we change this program to:


import java.util.Scanner;

public class Main {

  private static String inputString() {
    final Scanner scanner = new Scanner(System.in);
    String result = "";
    if (scanner.hasNextLine()) {
      result = scanner.nextLine();
    }
    scanner.close();
    return result;
  }

  public static void main(String[] args) {
    System.out.println("input fullName: ");
    final String value = inputString();
    System.out.println("fullName: " + value);
  }
}

/** Output:

input fullName:
John
fullName: hello

**/

Uh huh, it also works well, we just close Scanner after used it.

Now we want to input 2 values for first name and last name of user, so we change program to:


import java.util.Scanner;

public class Main {

  private static String inputString() {
    final Scanner scanner = new Scanner(System.in);
    String result = "";
    if (scanner.hasNextLine()) {
      result = scanner.nextLine();
    }
    scanner.close();
    return result;
  }

  public static void main(String[] args) {
    System.out.println("input firstName: ");
    final String firstName = inputString();
    System.out.println("input lastName: ");
    final String lastName = inputString();
    System.out.println("fullName: " + firstName + " " + lastName);
  }
}

/** Output:

input firstName: 
John
input lastName: 
fullName: John 

**/

What? The program ended without waiting us to input last name? What happen?

Let’s change a bit of method inputString(), we don’t check scanner.hasNextLine() and run it to see what happen:


private static String inputString() {
  final Scanner scanner = new Scanner(System.in);
  String result = "";
  // if (scanner.hasNextLine()) {
    result = scanner.nextLine();
  // }
  scanner.close();
  return result;
}

And output: Exception Image

Now, we can figure out about this problem. The program cannot receive anymore value from user!

But why? The reason is because why had closed it! Yes we had closed the way to receive input from user.

Where? It is:

scanner.close();

But we only closed the scanner of a local variable scanner, why it affects the whole program? Because, Scanner works base on System.in. And when we call scanner.close(), it also closes InputStream of System.in. When System.in has closed, we can reuse it in another Scanner to get input from user. So, when we call scanner.nextLine(), an exception will occur: NoSuchElementException in this case.

The problem will also happen when we use it with resource auto-closing with try-catch like this:


private static String inputString() {
  String result = "";
  try(Scanner scanner = new Scanner(System.in)){
    // if (scanner.hasNextLine()) {
      result = scanner.nextLine();
    // }
  }catch(Exception ex){
    ex.printStacktrace();
  }
  return result;
}

It happens because resource auto-closing will auto call method close when go out of try-catch block.

Use Scanner in carefully

Never close Scanner in your program :)

Let re-write it:


import java.util.Scanner;

public class Main {

    private static String inputString() {
        final Scanner scanner = new Scanner(System.in);
        String result = "";
        if (scanner.hasNextLine()) {
            result = scanner.nextLine();
        }
//        scanner.close();
        return result;
    }

    public static void main(String[] args) {
        System.out.println("input firstName: ");
        final String firstName = inputString();
        System.out.println("input lastName: ");
        final String lastName = inputString();
        System.out.println("fullName: " + firstName + " " + lastName);
    }

}

/** Output:

input firstName: 
John
input lastName: 
Matt
fullName: John Matt

**/

Ok, that is. Now it works well :)


comments powered by Disqus