0%

Java流程控制

用户交互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(); // hello
String word2 = strScanner.next(); // world
int num = strScanner.nextInt(); // 123

Scanner的进阶使用

分隔符和正则表达式

Scanner默认用空格作为分隔符,但可以自行定义:

  • useDelimiter(String pattern): 设置分隔符,支持正则表达式

读取CSV数据:CSV数据是一种通用的纯文本格式,用于以表格形式存储数据,其中每行代表一条记录,字段(列)之间用逗号或其他特定字符分隔

1
2
3
Scanner csvScanner = new Scanner("apple,banana,cherry");
csvScanner.useDelimiter(",");
String fruit1 = csvScanner.next(); // "apple"

异常处理和输入验证

为了鲁棒性,使用循环验证输入:
分析:使用 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);

// 如果输入的内容非法,则使用next()进行消耗掉
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:
// code
break;
case value2:
// code
break;
default:
// code

}
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("无效");
}
  • 用 -> 代替 : 和 break,更简洁

While 循环

While 的基本概念

While循环在条件为真时重复执行代码块

1
2
3
while (condition) {
// code
}
  • 条件先检查,后执行(可能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 {
// code
} 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) {
// code
}
  • 适用于数组、List 等 Iterable

数组遍历

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); // 15
}
}

结合集合

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 + " ");
} // 输出 1 2 3 4
}
}

标签化 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 + " "); // 1 3 5 7 9
}
}
}

标签化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 + " ");
}
} // 输出 1 1 1

goto

Java保留goto关键字但是不可用,标签化break/continue是替代方案,避免spaghetti代码,但过度使用标签会降低可读性

  • 无标签 break 在 Switch:Switch 中 break 防止穿透。
  • 标签位置:标签必须在循环前,且冒号后无空格。
  • 滥用:优先用方法重构代替标签。