用户交互Scanner
Scanner的基本概念
Scanner是Java.util包中的一个类,用于从各种各种输入源(如键盘、文件)读取数据,它允许程序与用户交互,读取不同类型的数据。Scanner就像一个”扫描仪”,它逐行或逐词解析输入:
要使用Scanner,首先需要导入:
1
| import java.util.Scanner;
|
创建Scanner对象
- 最常见的用法是从System.in(标准输入,即键盘)创建Scanner对象:
1
| Scanner scanner = new Scanner(System.in);
|
- 这会创建一个Scanner实例,准备从用户输入读取数据。
读取基本数据类型
Scanner提供多种方法来读取不同类型的数据:
nextInt():读取整数
nextDouble(): 读取双精度浮点数
nextLine(): 读取整行字符串(包括空格)
next(): 读取下一个词(以空格分隔)
nextBoolean(): 读取布尔值
- 需要注意的是,这些方法会阻塞程序,直到用户输入并按返回键
1 2 3 4 5 6 7 8 9 10
| public void runProcessControl() { Scanner scanner = new Scanner(System.in);
System.out.println("请输入你的姓名: "); String name = scanner.nextLine(); System.out.println("请输入你的年龄: "); int age = scanner.nextInt(); System.out.println("你好," + age + "岁的" + name); }
|
需要注意的是:
- 输入类型不匹配:如果用户输入字符串但程序用 nextInt(),会抛 InputMismatchException。解决:使用 try-catch 捕获异常,或先用 nextLine() 读取再解析。
- 未关闭 Scanner:在程序结束时调用 scanner.close(),否则可能导致资源泄漏。
- 多行输入问题:nextInt() 后如果跟 nextLine(),nextLine() 可能会读取空行(因为 nextInt() 不消耗换行符)。解决:nextInt() 后加一个 nextLine() 来消耗换行。
拓展
Scanner还可以从文件或者字符串中读取数据,例如:从字符串读取:
1 2 3 4
| Scanner strScanner = new Scanner("hello world 123"); String word1 = strScanner.next(); String word2 = strScanner.next(); int num = strScanner.nextInt();
|
Scanner的进阶使用
分隔符和正则表达式
Scanner默认用空格作为分隔符,但可以自行定义:
useDelimiter(String pattern): 设置分隔符,支持正则表达式
读取CSV数据:CSV数据是一种通用的纯文本格式,用于以表格形式存储数据,其中每行代表一条记录,字段(列)之间用逗号或其他特定字符分隔
1 2 3
| Scanner csvScanner = new Scanner("apple,banana,cherry"); csvScanner.useDelimiter(","); String fruit1 = csvScanner.next();
|
异常处理和输入验证
为了鲁棒性,使用循环验证输入:
分析:使用 while 循环确保输入有效。catch 块处理异常,并用 next() 清除缓冲区。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
public void expandProcessControl() { String name = ""; int age = -1; Scanner scanner = new Scanner(System.in);
while (name.trim().length() <= 0) { System.out.println("请输入你的姓名: "); try { name = scanner.nextLine(); if (name.trim().length() <= 0) { System.out.println("输入为空, 请重新输入..."); } } catch (InputMismatchException e) { System.out.println("输入的姓名无效"); scanner.nextLine(); } }
while (age < 0) { System.out.print("请输入正整数年龄:"); try { age = scanner.nextInt(); if (age <= 0) { System.out.println("年龄不能为0或者负数!"); } } catch (InputMismatchException e) { System.out.println("输入无效,请输入整数!"); scanner.next(); } } System.out.println("您的姓名是:" + name); System.out.println("您的年龄是:" + age); scanner.close(); }
|
从文件读取
Scanner可以从文件读取:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import java.util.Scanner; import java.io.File; import java.io.FileNotFoundException;
public class FileScanner { public static void main(String[] args) { try { Scanner fileScanner = new Scanner(new File("date.txt")); while (fileScanner.hasNextLine()) { System.out.println(fileScanner.nextLine()); } fileScanner.close(); } catch (FileNotFoundException e) { System.out.println("文件未找到!"); } } }
|
- hasNextLine(): 检查是否有下一行。
- 适用于日志分析或数据导入。
- hasNextInt(): 检查下一个输入是否为整数,避免异常。
- skip(Pattern pattern): 跳过匹配的输入。
顺序结构
顺序结构是程序的基本执行方式:代码从上到下逐行执行,没有分支或者循环,Java中的main方法默认就是顺序结构。
1 2 3 4 5 6 7 8
| public class Sequential { public static void main(String[] args) { int a = 5; int b = 10; int sum = a + b; System.out.println("和是:" + sum); } }
|
if 选择结构
if结构的基本概念
if语句根据条件执行代码块,语法:
- 单 if :
if (condition) { ... }
- if-else:
if (condition) { ... } else { ... }
- if-else if-else:多条件链
条件是布尔表达式,如 x > 0
单 if 结构
1 2 3 4 5 6 7 8
| public class IfBasic { public static void main(String[] args) { int score = 85; if (score >= 60) { System.out.println("及格!"); } } }
|
嵌套 if 和多条件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import java.util.Scanner;
public class IfAdvanced { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("输入分数:"); int score = scanner.nextInt(); if (score >= 90) { System.out.println("优秀"); } else if (score >= 80) { System.out.println("良好"); } else if (score >= 60) { System.out.println("及格"); } else { System.out.println("不及格"); if (score < 40) { System.out.println("需要补考"); } } scanner.close(); } }
|
Switch 选择结构
Switch 的基本概念
Switch根据表达式值匹配case执行代码,支持 int、byte、short、char、String、枚举
语法:
1 2 3 4 5 6 7 8 9 10 11
| switch (expression) { case value1: break; case value2: break; default:
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import java.util.Scanner;
public class SwitchBasic { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("输入月份(1-12):"); int month = scanner.nextInt(); switch (month) { case 1: case 2: case 12: System.out.println("冬季"); break; case 3: case 4: case 5: System.out.println("春季"); break; case 6: case 7: case 8: System.out.println("夏季"); case 9: case 10: case 11: System.out.println("秋季"); default: System.out.println("无效月份"); } scanner.close(); } }
|
分析:无 break 会“穿透”执行下一个 case,常用于分组
Java14+增强了Switch语法
1 2 3 4 5
| switch (day) { case "Monday" -> System.out.println("工作日"); case "Saturday", "Sunday" -> System.out.println("周末"); default -> System.out.println("无效"); }
|
While 循环
While 的基本概念
While循环在条件为真时重复执行代码块
- 条件先检查,后执行(可能0次执行)
1 2 3 4 5 6 7 8 9 10 11
| public class WhileBasic { public static void main(String[] args) { int sum = 0; int i = 1; while (i <= 100) { sum += i; i++; } System.out.println(sum); } }
|
无限循环和嵌套While
无限循环:While (true) { ... }, 用break退出
1 2 3 4 5 6 7 8 9 10 11
| int row = 1; while (row <= 5) { int col = 1; while (col <= row) { System.out.print("*"); col++; } System.out.println(); row++; }
|
DoWhile 循环
DoWhile 先执行代码块,再检查条件。至少执行一次。语法:
1 2 3
| do { } while (condition);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import java.util.Scanner;
public class DoWhileBasic { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int choice; do { System.out.println("菜单:1. 选项A 2. 选项B 0. 退出"); choice = scanner.nextInt(); switch (choice) { case 1: System.out.println("执行A"); break; case 2: System.out.println("执行B"); break; } } while (choice != 0); scanner.close(); } }
|
与 While 的比较
While 可能0次,DoWhile 至少1次。适合先行动后检查,如菜单
增强for循环
增强for的基本概念
增强for (for-each)遍历集合或数组,无需索引,语法:
1 2 3
| for (Type element : collection) { }
|
数组遍历
1 2 3 4 5 6 7 8 9 10
| public class ForEachBasic { public static void main(String[] args) { int[] numbers = {1, 2, 3, 4, 5}; int sum = 0; for (int num : numbers) { sum += num; } System.out.println("和:" + sum); } }
|
结合集合
1 2 3 4 5 6 7 8 9 10 11 12
| import java.util.ArrayList;
public class ForEachList { public static void main(String[] args) { ArrayList<String> fruits = new ArrayList<>(); fruits.add("Apple"); fruits.add("Banana"); for (String fruit : fruits) { System.out.println(fruit); } } }
|
增强for的局限性
- 不能修改集合:遍历中 add/remove 会抛 ConcurrentModificationException。
- 无索引:如果需要索引,用传统 for。
- 只读:element 是拷贝,不能修改原数组元素(对于基本类型)。
break
break的基本概念
break终止当前循环或Switch,跳出到外层
1 2 3 4 5 6 7 8 9 10
| public class BreakBasic { public static void main(String[] args) { for (int i = 1; i <= 10; i++) { if (i == 5) { break; } System.out.print(i + " "); } } }
|
标签化 break
Java无goto, 但标签化 break/continue 可跳出多层循环
1 2 3 4 5 6 7 8 9 10 11 12
| public class LabelBreak { public static void main(String[] args) { outer: for (int i = 1; i <= 3; i++) { for (int j = 1; j <= 3; j++) { if (j == 2) { break outer; } System.out.println("j = ", j); } } } }
|
continue
continue的基本概念
continue 跳过当前迭代,继续下一次
1 2 3 4 5 6 7 8 9 10
| public class ContinueBasic { public static void main(String[] args) { for (int i = 1; i <= 10; i++) { if (i % 2 == 0) { continue; } System.out.print(i + " "); } } }
|
标签化continue
1 2 3 4 5 6 7 8
| outer: for (int i = 1; i <= 3; i++) { for (int j = 1; j <= 3; j++) { if (j == 2) { continue outer; } System.out.print(j + " "); } }
|
goto
Java保留goto关键字但是不可用,标签化break/continue是替代方案,避免spaghetti代码,但过度使用标签会降低可读性
- 无标签 break 在 Switch:Switch 中 break 防止穿透。
- 标签位置:标签必须在循环前,且冒号后无空格。
- 滥用:优先用方法重构代替标签。