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
.