#java #try-catch #字符串解析 #扫描原生方法
摘要:
在 Java 交互式练习中,处理用户输入是新手的必修课。是用 scanner.nextInt() 原生方法,还是先读 String 再解析?本文将通过对比分析,揭示原生方法背后的死循环陷阱 ,并分享一种更稳健的万物皆文本编程思维。
代码
第一种:字符串解析法
java
public class TryCatchExercise04 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int num = 0;
String inputStr; // 变量定义在循环外
while (true) {
System.out.println("请输入一个整数:");
inputStr = scanner.next(); // 或者建议使用 scanner.nextLine()
try {
num = Integer.parseInt(inputStr);
break; // 解析成功,跳出循环
} catch (NumberFormatException e) {
System.out.println("你输入的不是整数,请重新输入");
}
}
System.out.println("输入的值 = " + num);
}
}
第二种:原生扫描法
java
public class TryCatchExercise04 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个整数:");
int num;
while (true) {
try {
num = scanner.nextInt();
break;
} catch (InputMismatchException e) {
System.out.println("你输入的不是整数,请重新输入");
// 关键救命稻草:清空缓冲区
scanner.nextLine();
}
}
System.out.println("输入的值 = " + num);
}
}
代码分析
Snippet 1: 字符串解析派(推荐 ✅)
java
// 先拿 String,再转 int
inputStr = scanner.next();
num = Integer.parseInt(inputStr);
-
核心优势:无残留,不卡死。
-
scanner.next()的逻辑是"不管输入的是人话还是鬼话,我先把它作为一个字符串吞掉"。 -
这意味着 :只要这行代码执行了,那个错误的输入(比如 "abc")就已经从 Scanner 的缓冲区里移走了。
-
容错率高 :即使在
catch块里啥都不写(虽然不推荐),程序也不会死循环,因为它已经把错误数据消费掉了。 -
更严谨的做法 :使用
scanner.nextLine()一次读取一整行,彻底防止由于空格导致的残留。
-
-
异常类型 :
NumberFormatException。这是 Java 中专门针对"数据格式不对"的标准异常,语义非常清晰。
Snippet 2: 原生扫描派(慎用 ⚠️)
java
// 直接拿 int,出错清空缓冲区
try {
num = scanner.nextInt();
} catch (InputMismatchException e) {
scanner.nextLine(); // 关键救命稻草
}
-
致命弱点:著名的"死循环陷阱"。
-
scanner.nextInt()的逻辑是:"我看一眼缓冲区里的下一个东西,如果是整数我就拿走;如果不是整数,我就不动它,也不报错,但我也不拿走,然后抛个异常。" -
这意味着:那个错误的 "abc" 还赖在 Scanner 的缓冲区里!
-
必须手动清空 :代码里的
scanner.nextLine()是必须写的。如果忘了写这句(新手极易犯错),程序就会陷入死循环 :nextInt失败 -> 抛异常 -> 捕获 -> 下一次循环nextInt又看到同一个 "abc" -> 又失败...... 控制台瞬间爆炸。
-
-
潜在 Bug :
scanner.nextLine()会吞掉这一行剩余的所有内容(包括回车)。如果用户输入abc 123,nextLine()可能会把后面本来有效的123也误杀清理掉,导致用户体验不仅是"重新输入",而是"我刚才输的一堆怎么都没了"。
总结:
-
Snippet 1 (String转换法) :推荐。
-
优点:逻辑解耦(先获取,再校验),没有缓冲区残留问题,代码健壮性强。
-
缺点:多生成一个 String 对象(性能损耗在交互式程序里可忽略不计)。
-
-
Snippet 2 (Scanner原生法) :慎用。
-
优点 :看起来比较"原生",不用显式调用
parseInt。 -
缺点 :极其依赖
catch块里的"清空缓冲区"操作。一旦漏写或写错(比如用next()还是nextLine()),就是死循环车祸现场。
-
延伸:怎么才能自然地写出字符串解析法呢?
建议:
以后做这种"用户输入直到正确为止"的功能,无脑选第一种。它是防止 Scanner 发疯的最好办法。
在初学阶段,教材和老师告诉我们:"想要整数?用 nextInt();想要小数?用 nextDouble()"。这就像给了一把锤子,告诉我们"钉子用这个敲"。要自然地 写出第一种(String 转 int),需要完成一次"认知升级"。你需要把大脑里的"默认设置"改一下。
以下是养成这种习惯的 3 个方法:
方法 1:万物皆文本 (Everything is String)
建立的第一个直觉:
用户在键盘上敲的永远是字符。即便他敲的是 1,那也是字符 '1',而不是数字 1。
-
旧思维 :用户要输年龄 -> 年龄是
int-> 找个能直接读int的方法 ->scanner.nextInt()。 -
新思维 :用户要输东西 -> 所有的输入本质都是一串 String -> 先把这串 String 抓手里 ->
scanner.next()-> 然后自己来把它变成int。
实战暗示:
以后只要涉及到 I/O(输入输出),无论是控制台、以后读文件、还是做网页表单,先全部当成 String 读进来,这是最安全的"防化服"。
方法 2:快递拆箱理论 (分步处理)
想象你在收快递。
-
第二种写法(nextInt):
你闭着眼睛直接把手伸进快递箱里摸。如果箱子里装的是仙人掌(错误数据),你的手就扎烂了(异常),还得去处理伤口(清空缓冲区)。
-
第一种写法(String + parse):
-
接快递 (
scanner.next()):不管箱子里是什么,先完整的抱进屋。这一步绝对不会出错,因为不管什么都能打包成盒子(String)。 -
拆箱检查 (
Integer.parseInt()):在桌子上把盒子拆开。-
是宝贝(数字)?拿走用。
-
是垃圾(乱码)?直接把盒子扔了(catch 报错),此时你手里是干干净净的,不需要再去外面箱子里掏垃圾了。
-
-
养成习惯:
把 "获取数据" 和 "校验数据" 分成两步走,不要试图一步到位。
方法 3:Web 开发的预演
这是最功利的一个理由,能逼自己马上改习惯。
在以后要学的 Web 开发(Spring Boot) 中,前端网页传给后端的 JSON 数据,或者 HTTP 请求参数,本质上全都是字符串。
-
并没有
request.getInt()这种东西(虽然框架封装了,但底层全是 String)。 -
现在练习
Integer.parseInt(String s),其实是在为以后写 Web 接口打基本功。
刻意练习:三步走口诀
下次再写 Scanner,强迫自己按这个**"三步走"**节奏来写,写几次就自然了:
-
"拿进来":
javaString str = scanner.next(); // 闭眼拿,全是 String -
"变一下":
javaint num = Integer.parseInt(str); // 尝试变身 -
"兜个底":
java// 变身失败了?包个 try-catch try { int num = Integer.parseInt(scanner.next()); break; // 成功就溜 } catch (NumberFormatException e) { // 失败就报错 }
当不再相信 Scanner 提供的那些"贴心"的 nextXxx 方法,而是相信自己手里的 String 时,就自然会写出第一种代码了。