必须先调用hasnextxxx()预检再调nextxxx(),否则抛nosuchelementexception;scanner关闭后调用会抛illegalstateexception;try-catch仅适用于明确有fallback的场景,优先用预检而非异常处理。

Scanner.next() 调用前不检查 hasNext() 就会炸
Java 里 Scanner 抛 NoSuchElementException 最常见场景:没确认输入是否存在,直接调 next()、nextInt() 这类“取值即消费”的方法。它不是“没读到就返回 null”,而是直接崩——因为设计上就假设你已用 hasNextXxx() 预检过。
典型错误写法:
Scanner sc = new Scanner(System.in); int x = sc.nextInt(); // 用户按 Ctrl+D 或输完就崩
- 必须配对使用:
hasNextInt()→nextInt(),hasNextLine()→nextLine() -
hasNext()只判断是否有任意 token(空格分隔),不保证类型;想读 int 就得用hasNextInt() - 如果输入源是文件且提前 EOF,同样触发该异常,不能只当“用户手抖”处理
Iterator.next() 在循环里裸奔等于自爆
Iterator 的 next() 和 Scanner.next() 行为一致:不检查就调,必抛 NoSuchElementException。很多人写 for-each 很顺,但一换成手动 Iterator 就忘掉 while 条件。
错例:
Iterator<string> it = list.iterator();
while (it.hasNext()) {
// 忘了 hasNext() 判断,直接 next()
String s = it.next(); // ✅ 正确位置
process(s);
}
// 但下面这种就危险:
String first = it.next(); // ❌ it 可能刚创建就空</string>
- 每次调
next()前,必须确保刚执行过hasNext()且返回true - 不要跨多次循环复用同一个
Iterator对象,尤其在异常分支后继续用 - 并发修改集合(如边遍历边
remove())可能让hasNext()返回true但next()仍抛异常——这时要捕获ConcurrentModificationException,不是NoSuchElementException
try-catch 不是防护,是补救;真防护靠预检
有人习惯包一层 try-catch(NoSuchElementException e) 来“兜底”,这看似省事,实则掩盖逻辑缺陷。异常是控制流中断,开销大,且无法区分“真没数据”和“本该有却丢了”的情况。
- 预检(
hasNextXxx()/hasNext())是零成本的布尔判断,性能远优于抛异常 - catch 块里若只打印日志或忽略,会导致后续逻辑用默认值硬扛,bug 更难定位
- 唯一合理用 catch 的场景:你明确知道某次调用可能无数据,且有 fallback 策略(如用默认值、重试、跳过),但仍建议优先用预检 + if 分支
Scanner 关闭后还调用 next()?别让资源泄漏伪装成 NoSuchElementException
Scanner 关闭后(close()),再调任何 nextXxx() 方法,抛的不是 NoSuchElementException,而是 IllegalStateException。但新手常误判为前者,尤其在 finally 块里关了 scanner,后面又不小心引用了它。
- 确认异常栈顶是不是
java.util.Scanner.<em>throwFor</em>()—— 是它才是NoSuchElementException;如果是java.util.Scanner.<em>ensureOpen</em>(),就是已关闭 - 避免在方法间传递未管理生命周期的
Scanner;推荐用 try-with-resources 自动关闭 - 用
System.in构造的Scanner,关闭后System.in也会被关——后续任何读 stdin 的操作全失效,这个坑比异常本身更麻烦
真正难防的不是语法错误,是那种“看起来走通了,但某次输入边界刚好绕过预检逻辑”的情况。多一重 hasNextXxx() 判断,比事后抓异常靠谱得多。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











