
关闭基于 System.in 的 Scanner 会永久关闭标准输入流,导致后续读取失败并引发死循环;根本解决方案是绝不关闭该 Scanner 实例,而应复用单个 Scanner 对象贯穿整个程序生命周期。
关闭基于 `system.in` 的 scanner 会永久关闭标准输入流,导致后续读取失败并引发死循环;根本解决方案是**绝不关闭该 scanner 实例**,而应复用单个 scanner 对象贯穿整个程序生命周期。
在 Java 中,Scanner 是一个包装器(wrapper),它本身不持有底层 I/O 资源,而是委托给其关联的 Readable 源(如 System.in)。根据官方 Javadoc,调用 Scanner.close() 时,若其底层源实现了 Closeable(InputStream 确实实现),则会级联关闭该源——这意味着 System.in 将被彻底关闭。
而 System.in 一旦关闭,无法重新打开。JVM 启动时初始化的 System.in 是单例且不可重置的静态流。因此,任何在循环中反复创建/关闭 Scanner(System.in) 的尝试(例如在 input() 方法内新建并关闭 Scanner),都会导致第二次及之后的 Scanner 构造失败或 hasNextXxx() 行为异常(如始终返回 false 或阻塞失效),最终使 while(true) 陷入无输入响应的“伪活跃”死循环。
✅ 正确做法:全局复用一个未关闭的 Scanner 实例
你的原始代码已接近最优解——将 Scanner userInput = new Scanner(System.in) 创建于 main 方法开头,并作为参数传入 input() 方法。这完全符合规范,无需修改循环条件。只需确保:
- ✅ 绝不调用 userInput.close()(包括不在 input() 内、不在 finally 块中、也不在 try-with-resources 中);
- ✅ 在 input() 中妥善处理非整数输入(你已用 userInput.next() 消费非法 token,逻辑正确);
- ✅ 使用 hasNext("q") 前确保已跳过空白(Scanner 默认跳过分隔符,通常安全;但为健壮性,可补充 userInput.skip("\s*"))。
以下是优化后的完整示例(修复潜在空格匹配问题,增强健壮性):
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner userInput = new Scanner(System.in); // ← 单例,永不关闭!
while (true) {
int number = input(userInput);
switch (number) {
case 0:
System.out.println("Invalid input. Enter an integer or q to Quit");
break;
case -1:
System.out.println("You chose to quit. Good-bye");
System.exit(0);
break;
default:
System.out.println("You entered " + number);
break;
}
}
}
private static int input(Scanner scanner) {
System.out.print("Enter an integer or q to Quit the program: ");
if (scanner.hasNextInt()) {
return scanner.nextInt();
} else if (scanner.hasNext("q")) {
scanner.next(); // 消费 "q"
return -1;
} else {
// 消费任意非整数、非"q"的 token(如 "abc", "42x", 空行等)
scanner.next();
return 0;
}
}
}⚠️ 注意事项总结:
- 禁止关闭 System.in 关联的 Scanner:这是本问题唯一根源,也是唯一必须遵守的约束;
- 不要试图用 new Scanner(System.in) 在循环内重建 Scanner——每次构造都依赖未关闭的 System.in,而首次关闭后它已失效;
- 若需处理文件或字符串等其他输入源,请放心使用 try-with-resources;仅 System.in(及 System.out/System.err)是特例;
- System.exit(0) 虽能终止程序,但在大型应用中建议改用 break 配合外层循环控制,以利于资源清理和测试。
遵循“一个 Scanner,全程复用,永不关闭”的原则,即可彻底规避无限循环,写出稳定可靠的交互式控制台程序。

















