Java 进阶之路(四):与用户“交流”和程序的“循环”与“判断” 🗣️🔄
探险家们,你们已经掌握了 Java 的基础工具和简单的决策方法。现在,我们将学习如何让你的程序变得更加“有生命”,能够接收外部世界的输入,并根据更复杂的规则执行重复的任务。
🤝 与用户“握手”:Scanner 接收输入
让你的程序能够与用户互动,是构建实用应用程序的第一步。在 Java 中,我们可以使用 Scanner
类来接收用户在控制台输入的数据。
这就像给你的程序装上了一只“耳朵”,可以听到用户说的话。
虽然在面向对象深入学习之前,我们可能无法完全理解 Scanner
的底层原理,但现在,你可以先记住使用它的三个关键步骤:
- 导入 Scanner 类: 告诉 Java 你要使用
Scanner
这个工具。在代码文件的顶部添加这行:import java.util.Scanner; // 导入一个扫描仪
- 创建 Scanner 对象: 实例化一个
Scanner
对象,就像制造一台“扫描仪”准备使用。
这里的Scanner scan = new Scanner(System.in); // 新建一个扫描仪,名字叫 scan
System.in
表示从标准输入流(通常是键盘)读取数据。 - 使用 Scanner 方法读取输入: 根据你需要接收的数据类型,调用
Scanner
对象相应的方法。scan.nextInt()
: 读取一个整数。scan.nextDouble()
: 读取一个小数。- 还有其他方法可以读取字符串等等(后续会学习)。
System.out.println("请输入年龄:"); int age = scan.nextInt(); // 扫描用户输入的整数并存入 age 变量 System.out.println("请输入商品价格:"); double price = scan.nextDouble(); // 扫描用户输入的小数并存入 price 变量
掌握这三步,你的程序就可以接收用户输入的数据,并进行后续的处理了!
🚦 更高效的“多路选择”:switch...case 结构
上次我们学习了 if...else if...else
结构来处理多条分支。当你的选择是基于一个变量的固定数值时,switch...case
结构通常是更优雅、效率更高、结构更清晰的选择。
想象一下,你的程序需要根据用户输入的数字来执行不同的操作,比如一个简单的菜单选项。
-
优点:
- 效率高: 对于基于固定数值的判断,
switch
的执行速度通常比if...else if
更快。 - 结构清晰: 代码逻辑更易读懂,一眼就能看出不同数值对应哪些操作。
- 效率高: 对于基于固定数值的判断,
-
缺点:
- 只能对整数判断相等:
switch
只能用于对 byte, short, int, char, String 和 枚举 (enum) 类型进行相等判断。不能用于判断大小范围或者其他复杂条件。
- 只能对整数判断相等:
-
break
的作用: 在switch
结构中,每个case
后面通常都需要加上break;
语句。break
的作用是跳出当前的switch
结构。如果没有break
,程序会继续执行下一个case
的代码(这叫做“fall-through”),直到遇到break
或switch
结构结束。 -
switch 中可用的数据类型: byte, short, int, char, String, 枚举 (enum)。
示例:一个简单的命令解析程序
import java.util.Scanner;
public class CommandBySwitch {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.println("请选择功能: 1.存款 2.取款 3.查询余额 4.退卡");
int command = scan.nextInt();
switch (command) { // 根据用户输入的 command 值进行分支
case 1: // 如果 command 等于 1
System.out.println("存款业务...");
break; // 执行完后跳出 switch
case 2: // 如果 command 等于 2
System.out.println("取款业务...");
break; // 执行完后跳出 switch
case 3: // 如果 command 等于 3
System.out.println("查询余额业务...");
break; // 执行完后跳出 switch
case 4: // 如果 command 等于 4
System.out.println("退卡成功");
break; // 执行完后跳出 switch
default: // 如果 command 不是上面任何一个 case 的值
System.out.println("输入错误");
// default 后面通常不需要 break,因为它已经是最后一个分支了
}
}
}
通过这个例子,你可以看到 switch
结构如何清晰地处理基于特定数值的选择。
🔄 重复执行任务:循环结构
在编程中,我们经常需要重复执行一段相同的或者相似的代码。比如,打印一串数字、计算一系列数据的总和、或者在游戏中反复检查玩家的状态。这时候,我们就需要用到循环结构。
循环的本质就是反复多次执行一段代码块。
-
循环的三要素 🔑🔑🔑: 理解循环的三要素,是写出正确循环的关键,非常重要!
- 循环变量的初始化: 在循环开始之前,你需要给一个变量设置一个起始值。这个变量将用来控制循环的执行。
- 循环的条件 (以循环变量为基础): 这是一个布尔表达式,用来判断循环是否应该继续执行。只要条件为
true
,循环就会继续;当条件为false
时,循环结束。 - 循环变量的改变: 在每次循环执行结束后,你需要改变循环变量的值,以便最终使循环条件变为
false
,从而结束循环。否则,就会陷入死循环!
循环变量,顾名思义,就是在整个循环过程中那个会反复改变的数字或值。
举例:输出 5 次 "行动是成功的阶梯"
- 循环变量:次数
times
- 初始化:
int times = 0;
(或者int times = 1;
等等,只要能控制循环次数就行) - 条件:
times < 5
(或者times <= 5
等等,取决于你的初始化) - 改变:
times++;
举例:输出 9 的乘法表
- 循环变量:乘数
num
- 初始化:
int num = 1;
- 条件:
num <= 9
- 改变:
num++;
(如果你想输出奇数乘法表,就是num += 2;
)
🚪 不同类型的循环结构:while, do...while, for
Java 提供了三种主要的循环结构,它们在执行流程上略有不同,适用于不同的场景。
-
while
结构:先判断后执行 🤔➡️-
语法:
while (布尔表达式) { 循环体 // 重复执行的代码 }
-
执行过程:
- 先判断布尔表达式的值。
- 如果为
true
,执行循环体。 - 执行完循环体后,再次回到第 1 步,重新判断布尔表达式的值。
- 如此反复,直到布尔表达式的值为
false
时,循环结束。
-
特点:
while
循环是先判断后执行的,所以有可能一次循环体都不会执行(如果第一次判断条件就为false
)。
示例:输出 5 次 "行动是成功的阶梯"
int times = 0; // 1. 循环变量初始化 while (times < 5) { // 2. 循环条件 System.out.println("行动是成功的阶梯"); times++; // 3. 循环变量改变 } System.out.println("继续执行...");
示例:猜数字小游戏 (使用
while
循环)import java.util.Scanner; public class GuessingWhile { public static void main(String[] args) { Scanner scan = new Scanner(System.in); int num = (int)(Math.random() * 1000 + 1); // 生成 1 到 1000 的随机数 System.out.println("(作弊:正确数字是 " + num + ")"); System.out.println("猜吧!"); int guess = scan.nextInt(); // 1. 猜数字变量初始化 while (guess != num) { // 2. 猜的数字不等于正确数字时继续循环 if (guess > num) { System.out.println("猜大了"); } else { System.out.println("猜小了"); } System.out.println("再猜一次!"); guess = scan.nextInt(); // 3. 猜数字变量改变 (读取新的输入) } System.out.println("恭喜你猜对了!"); } }
-
-
do...while
结构:先执行后判断 ➡️🤔-
语法:
do { 循环体 // 重复执行的代码 } while (布尔表达式); // 注意这里有一个分号
-
执行过程:
- 先执行一次循环体。
- 然后判断布尔表达式的值。
- 如果为
true
,回到第 1 步,再次执行循环体。 - 如此反复,直到布尔表达式的值为
false
时,循环结束。
-
特点:
do...while
循环是先执行后判断的,所以至少会执行一次循环体,即使第一次判断条件就为false
。 -
适用场景: 当你的循环变量的初始化和循环变量的改变的代码是相同的时候,或者你至少需要执行一次循环体的时候,可以优先考虑使用
do...while
。
示例:猜数字小游戏 (使用
do...while
循环) 在这个例子中,第一次猜数字的代码和之后每次再次猜数字的代码是一样的(都是读取用户输入),所以使用do...while
更简洁。import java.util.Scanner; public class GuessingDoWhile { public static void main(String[] args) { Scanner scan = new Scanner(System.in); int num = (int)(Math.random() * 1000 + 1); System.out.println("(作弊:正确数字是 " + num + ")"); int guess; // 声明猜数字变量,但在 do 块中初始化和改变 do { System.out.println("猜吧!"); guess = scan.nextInt(); // 1+3. 猜数字变量的初始化和改变都在这里 if (guess > num) { System.out.println("猜大了"); } else if (guess < num) { System.out.println("猜小了"); } else { System.out.println("恭喜你猜对了!"); } } while (guess != num); // 2. 猜的数字不等于正确数字时继续循环 (注意这里的分号) } }
-
-
for
结构:应用率最高,适合与次数相关的 🔢🔄-
语法: 将循环三要素集中写在一起,结构紧凑,非常常用。
for (循环变量初始化; 循环条件; 循环变量改变) { 循环体 // 重复执行的代码 }
执行过程: 1 -> 2 -> 4 -> 3 -> 2 -> 4 -> 3 -> ... 直到 2 (循环条件) 为
false
时结束。 -
特点:
for
循环通常用于已知循环次数或者循环次数可以根据循环变量的变化很容易推算出来的情况。是应用率最高的循环结构。
示例:计算 1 到 100 的累加和
int sum = 0; // 存放累加和的变量 for (int i = 1; i <= 100; i++) { // 1. i 从 1 开始; 2. i 小于等于 100 时循环; 3. i 每次加 1 sum = sum + i; // 累加 } System.out.println("1 到 100 的累加和为: " + sum);
注意:在
for
循环的圆括号中声明的循环变量(比如上面的int i
),它的作用域只在当前for
循环的大括号{}
内。 在for
循环结束后,你就无法再使用这个变量了。示例:输出 9 的乘法表 (使用
for
循环)for (int num = 1; num <= 9; num++) { System.out.println(num + "*9=" + num * 9); }
示例:输出 5 次 "行动是成功的阶梯" (使用
for
循环)for (int times = 0; times < 5; times++) { System.out.println("行动是成功的阶梯"); } System.out.println("继续执行...");
-
🗺️ 如何选择合适的循环结构?
当你需要使用循环时,可以按照这个思路来选择:
-
首先看循环是否与“次数”有关:
- 如果循环次数是已知或容易确定的(比如循环 10 次,打印 1 到 100 的数字),那么直接使用
for
循环。for
循环的结构非常适合这种情况。 - 如果循环次数不确定,而取决于某个条件的何时满足(比如猜数字游戏,不知道用户几次能猜对),那么你需要进一步判断:
- 如果循环次数是已知或容易确定的(比如循环 10 次,打印 1 到 100 的数字),那么直接使用
-
如果循环与次数无关,再看“循环变量的初始化”和“循环变量的改变”的代码是否相同:
- 如果相同(比如猜数字游戏中的读取用户输入),并且你需要确保循环体至少执行一次,那么优先选择
do...while
循环。 - 如果不同(比如读取一个未知长度的文件,循环条件是文件是否读到结尾),那么选择
while
循环。
- 如果相同(比如猜数字游戏中的读取用户输入),并且你需要确保循环体至少执行一次,那么优先选择
基础结构的基石 🧱
记住,任何复杂的程序逻辑都可以通过三种基本的结构来实现:
- 顺序结构: 代码从上往下,一行一行地执行,每一句都会被执行到。
- 分支结构: 根据条件,有选择地执行某段代码一次,不是每一句都必走。
- 循环结构: 根据条件,有选择地反复执行某段代码多次,也不是每一句都必走。
通过组合运用这三种结构,你几乎可以实现任何你想要的程序逻辑。
🔮 生成随机数的小技巧
在猜数字游戏中,我们用到了生成随机数。这里分享一下生成一个 1 到 1000 之间随机整数的方法:
int num = (int)(Math.random() * 1000 + 1); // 生成 1 到 1000 之内的随机整数
Math.random()
会生成一个 0.0 到 0.9999... 之间的双精度浮点数。- 乘以 1000,得到 0.0 到 999.999... 之间的数。
- 加上 1,得到 1.0 到 1000.999... 之间的数。
(int)
进行强制类型转换,小数部分被舍弃,最终得到 1 到 1000 之间的整数。
🧬 变量的“地盘”:作用域
最后补充一个概念:变量的作用域。简单来说,变量的作用域就是它“活着”的范围。
- 变量的作用域从它被声明的地方开始,到包含它最近的一对大括号
{}
结束。 - 在同一个作用域内,变量不能重名。
public class ScopeDemo { public static void main(String[] args) { int a = 10; // a 的作用域从这里开始 if (a > 5) { int b = 20; // b 的作用域从这里开始 System.out.println(a); // 可以在这里访问 a System.out.println(b); // 可以在这里访问 b } // b 的作用域在这里结束 System.out.println(a); // 可以在这里访问 a // System.out.println(b); // 错误!b 的作用域已经结束 } // a 的作用域在这里结束 }
理解变量的作用域,可以帮助你避免一些潜在的错误。
🏁 小结
- switchi能作用于哪些数据类型的数据
- while、.do…while、for的三种结构如何选择
- for循环会发生死循环的几种情况
- break与continuel的应用场景
期待与你再次启程!