是否应仅实例化一次Scanner?多次实例化报错原因及异常解析
嘿,这个问题挺有探究精神的,我来一步步给你拆解清楚:
一、报错的核心原因:关闭Scanner会连带关闭System.in
你代码里的第一个scanner.close()是关键!所有基于System.in创建的Scanner,本质上共享同一个底层标准输入流。当你调用close()方法关闭Scanner时,它会自动把关联的System.in也一并关闭——这是Java Scanner的设计逻辑,目的是确保资源被正确释放。
当你后面再创建Scanner sc = new Scanner(System.in)时,这个新Scanner关联的System.in已经是关闭状态了,根本没法从里面读取任何输入,所以调用sc.nextLine()的时候就会直接抛出异常。
如果把第一个scanner.close()注释掉,你的代码就能正常运行(虽然确实冗余,但不会报错)。
二、java.util.NoSuchElementException: No line found是什么意思?
这个异常的字面意思很直白:Scanner尝试读取一行输入,但找不到任何可读取的内容。具体到你的场景,就是因为底层的System.in已经被关闭,没有任何输入流可以读取了,Scanner在执行nextLine()时发现没有数据可读,也无法再获取新数据,就抛出了这个异常。
一般遇到这个异常,要么是输入流已经关闭,要么是输入已经被读完(比如文件读取到末尾),但你的情况属于前者。
三、为什么说Scanner不可克隆?
Scanner不能被克隆主要有两个原因:
- 没有实现
Cloneable接口:Java里要实现克隆,类必须实现Cloneable标记接口,并重写clone()方法,但java.util.Scanner根本没做这件事,直接调用clone()会抛出CloneNotSupportedException。 - 底层依赖的输入流无法克隆:Scanner依赖的输入流(比如
System.in)是有状态的、单向的流——它记录了当前读取的位置,而且流一旦被读取或关闭,状态就不可逆转。你没法复制一个和原输入流状态完全一致的副本,自然也就没法克隆出一个功能正常的Scanner。另外Scanner自身还有很多内部缓存、分隔符配置等状态,这些也很难被完整复制。
总结一下:重复创建Scanner本身不是问题,但关闭第一个Scanner会把共享的System.in关掉,导致后续所有基于它的Scanner都无法工作;而Scanner的设计从根上就不支持克隆,因为它依赖的资源和自身状态都不具备可复制性。
内容的提问来源于stack exchange,提问作者Paris

