This answer focuses on the close() operation and why there's no option to read again from System.in if a Scanner instance previously closed it, as the top answer already gave the correct info. Just if curious.
Scanner
When a Scanner is closed, it will close its input source if the source
implements the Closeable interface.
A Scanner is not safe for multithreaded use without external
synchronization.
- You should create a Scanner instance for each source you want to read from.
- If you must share the same instance, you should implement the synchronization mechanism, as it's not thread safe.
- As other answers already pointed,
close() is a "dangerous" operation.
System.in close()
Let's suppose System.in was specified as source.
This is the close method from the InputStreamReader
public void close() throws IOException
{
synchronized (lock)
{
// Makes sure all intermediate data is released by the decoder.
if (decoder != null)
decoder.reset();
if (in != null)
in.close();
in = null;
isDone = true;
decoder = null;
}
}
The variable that refers to System.in is the one called in.
Two operations are performed on this InputStream (besides the null check):
1. in.close()
This does nothing at all: System.in 's class (InputStream) just leaves an empty implementation of the inherited close() method (from Closeable)
/**
* Closes this stream. Concrete implementations of this class should free
* any resources during close. This implementation does nothing.
*
*/
public void close() throws IOException {
/* empty */
}
Not even the javadocs hide the truth:
The close method of InputStream does nothing.
2. in = null
This is the real reason why you won't be able to read again from System.in. Setting it to null unables any further read attempts from this source with a new Scanner.
But... why does it throw a NoSuchElementException instead of a NullPointerException?
The initialization of the Scanner doesn't fail in the step of creating its Reader instance. This is because the InputStream is wrapped into a new BufferedInputStream. So the lock Object is not null at the initialization of the Scanner's Reader:
public InputStreamReader(InputStream in)
{
super(in);
this.in = in;
...
}
.
protected Reader(Object lock)
{
if (lock == null) {
throw new NullPointerException();
}
this.lock = lock;
}
You'll be able to create a second Scanner instance from System.in without any exception being thrown; As the InputStream is wrapped into a new BufferedInputStream instance, the lock Object is not null and passes the filter. But the inner InputStream, System.in, is null indeed, from the moment it was set to null in the previous close() operation:

This is the lock Object in the second initialization of the Scanner for System.in. The Scanner still doesn't know something will go badly, as its initialization was succesfull (due to the wrapped BufferedInputStream) and still believes its InputStream is valid.
But at the first try for reading again from System.in, this happens:
public String nextLine() {
if (hasNextPattern == linePattern())
return getCachedResult();
clearCaches();
String result = findWithinHorizon(linePattern, 0);
if (result == null) /* result is null, as there's no source */
throw new NoSuchElementException("No line found");
(...)
}
That's the moment where Scanner notices at last that something is not going well. The result of findWithinHorizon will return null as there's no source from where to find.
As a result of previously setting System.in to null in the close() operation, you get the error when trying to read from the second Scanner instance: NoSuchElementException.