If you are using Java’s Scanner class for keyboard input then you might be doing it wrong.

Yesterday I gave my students a warm up exercise in my third year Java course. The students were free to use console, Swing or JavaFX for user input. Most chose console because they had minimal exposure to GUI coding and that is one of the main topics in my course. Within a few minutes some hands went up in regards to the use of the Scanner class. Here is a simplified version of what they were trying to do.

[pastacode lang=”java” manual=”public%20class%20App%20%7B%0A%0A%20%20%20%20public%20void%20perform()%20%7B%0A%20%20%20%20%20%20%20%20firstScanner()%3B%0A%20%20%20%20%20%20%20%20secondScanner()%3B%0A%20%20%20%20%7D%0A%20%20%20%20%0A%20%20%20%20public%20void%20firstScanner()%20%7B%0A%20%20%20%20%20%20%20%20String%20str%20%3D%20%22%22%3B%0A%20%20%20%20%20%20%20%20Scanner%20sc%20%3D%20new%20Scanner(System.in)%3B%0A%20%20%20%20%20%20%20%20System.out.println(%22Enter%20a%20string%3A%20%22)%3B%0A%20%20%20%20%20%20%20%20if%20(sc.hasNext())%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20str%20%3D%20sc.next()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20sc.nextLine()%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20sc.close()%3B%0A%20%20%20%20%20%20%20%20System.out.println(%22str%20%3D%20%22%20%2B%20str)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20public%20void%20secondScanner()%20%7B%0A%20%20%20%20%20%20%20%20int%20num%20%3D%200%3B%0A%20%20%20%20%20%20%20%20Scanner%20sc%20%3D%20new%20Scanner(System.in)%3B%0A%20%20%20%20%20%20%20%20System.out.println(%22Enter%20a%20number%3A%20%22)%3B%0A%20%20%20%20%20%20%20%20if%20(sc.hasNextInt())%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20num%20%3D%20sc.nextInt()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20sc.nextLine()%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20sc.close()%3B%0A%20%20%20%20%20%20%20%20System.out.println(%22num%20%3D%20%22%20%2B%20num)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20public%20static%20void%20main(String%5B%5D%20args)%20%7B%0A%20%20%20%20%20%20%20%20App%20app%20%3D%20new%20App()%3B%0A%20%20%20%20%20%20%20%20app.perform()%3B%0A%20%20%20%20%20%20%20%20System.exit(0)%3B%0A%20%20%20%20%7D%0A%7D%0A” message=”Scanner Demo” highlight=”” provider=”manual”/]

When this program executes it asks for the first String but never asks for the int. A run looks like:

[pastacode lang=”bash” manual=”Enter%20a%20string%3A%20%0Abob%0Astr%20%3D%20bob%0AEnter%20a%20number%3A%20%0Anum%20%3D%200%0A” message=”” highlight=”” provider=”manual”/]

It never stops to ask for the number and immediately prints out the default value for num. I had never seen this problem before. When I teach an introductory course I show my students that the Scanner for keyboard input should, in most cases, be a class variable initialized in the constructor. I also must admit that I never close the Scanner object when the keyboard is involved.

You can see that in the code the Scanner object is closed in each method. I didn’t think this was a problem and so I stared at the student’s code for a few minutes and then went to Google. There was not much to be found. Most of the information showed that when using Scanner to access a file you should close it. Most articles that used Scanner for keyboard input declared the Scanner in a method and closed it at the end of the method. There was one suggestion in a StackOverflow posting that suggested a potential problem with closing Scanner for keyboard input but it was not definitive.

My next experiment was to remove the close() statements. The Scanner performed as originally expected:

[pastacode lang=”bash” manual=”Enter%20a%20string%3A%20%0Abob%0Astr%20%3D%20bob%0AEnter%20a%20number%3A%20%0A23%0Anum%20%3D%2023″ message=”” highlight=”” provider=”manual”/]

So, back to research on Google where I uncovered a fact not mentioned in any JavaDocs or in any article from any source when discussing the Scanner class. If System.in, a BufferedInputStream from the keyboard, is closed, as happens when the Scanner object is closed, you are closing a System stream that cannot be re-opened. The program must be exited and then re-run to re-establish System.in.

I thought this was strange and looked deeper into how the keyboard was connected to System.in. This stream is established by private methods in the JVM. I did see suggestions as to how this could be re-established but I suspect that this code may have never been tested:

Suggestion #1

[pastacode lang=”bash” manual=”FileInputStream%20fdIn%20%3D%20new%20FileInputStream(FileDescriptor.in)%3B%0ASystem.setIn(new%20BufferedInputStream(fdIn))%3B%0A” message=”” highlight=”” provider=”manual”/]

Suggestion #2

[pastacode lang=”bash” manual=”System.setIn(new%20FileInputStream(FileDescriptor.in))%3B” message=”” highlight=”” provider=”manual”/]

Neither worked to re-establish the connection between System.in and the keyboard. If you know a way then I’d dearly like to know. For now:

DO NOT CLOSE A SCANNER OBJECT INITIALIZED WITH System.in !

 

About Ken Fogel

Java Champion, JCP Executive Committee Member, Vice-President of the Jakarta EE Ambassadors, NetBeans Dream Team Member, and conference speaker/organizer. Currently a Research Scholar in Residence at Dawson College after retiring from the classroom. Passionate about teaching and inspiring everyone to be better programmers.

2 thoughts on “If you are using Java’s Scanner class for keyboard input then you might be doing it wrong.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.