JavaSE(三)逻辑控制:if、switch、while、for 与输入输出

本文系统讲解JavaSE中核心逻辑控制结构,包括if分支判断(含闰年判定)、switch等值匹配(支持JDK14+箭头语法)、while与for循环(含阶乘及嵌套计算)、break/continue流程控制,以及Scanner键盘输入与三种输出方式。重点剖析边界条件、循环变量更新、死循环规避、输入缓冲区处理等常见陷阱,结合猜数字小项目整合实践。

专栏: JavaSE
系列导航: 一、数据类型 → 二、运算符 → 三、逻辑控制(本篇)
代码来源: 20261018/src/Test.java

前两篇把变量、数据类型和运算符准备好了。但程序如果只会从上到下顺序执行,还称不上“会思考”。真正让程序根据条件做选择、把某段代码重复执行的,是逻辑控制。

这篇笔记结合我练习代码里的闰年判断、switch、阶乘、break/continue、Scanner 和猜数字游戏,把 Java 的逻辑控制串一遍。

说明:练习文件里用 main1 到 main8 把示例分开,本文按知识点拆成独立片段。实际运行时,把对应代码放进 main 方法里即可。

逻辑控制到底在控制什么?

程序的基本执行结构可以分成三类:

结构代表语句作用
顺序结构普通语句从上到下依次执行
分支结构if、switch根据条件选择执行哪段代码
循环结构while、for、do-while条件满足时重复执行

除此之外,break、continue、return 可以改变循环或方法的执行流程,常和循环一起使用。

写逻辑控制时,最需要想清楚三件事:

  1. 边界在哪里:条件什么时候成立,什么时候不成立;
  2. 循环变量怎么变:每一轮有没有向结束条件靠近;
  3. 什么时候退出:是自然结束,还是 break、return 提前结束。

if:让程序学会选择

if 是最基础的分支语句:

if (布尔表达式) {
    // 条件成立时执行
} else if (布尔表达式) {
    // 另一个条件成立时执行
} else {
    // 以上条件都不成立时执行
}

几个要点:

  • if 括号里的结果必须是 boolean,不能写 if (1);
  • 同一组 if / else if / else 最多只会命中一个分支;
  • 多个彼此独立的 if 是可以同时成立的,不要和 else if 混淆;
  • 即使循环体或分支里只有一行代码,也建议保留大括号,后期加代码时不容易出错;
  • else 会和离它最近、还没有匹配的 if 结合,嵌套时尤其要注意。

例子:判断闰年

闰年的规则是:

  • 普通年份:能被 4 整除的是闰年;
  • 世纪年份:能被 100 整除时,还必须能被 400 整除才是闰年。

用嵌套 if 写出来:

int year = 2000;

if (year % 100 == 0) {
    // 世纪年:还要继续判断能不能被 400 整除
    if (year % 400 == 0) {
        System.out.println("是闰年");
    } else {
        System.out.println("不是闰年");
    }
} else {
    // 普通年份:能被 4 整除就是闰年
    if (year % 4 == 0) {
        System.out.println("是闰年");
    } else {
        System.out.println("不是闰年");
    }
}

这段代码的判断路径可以画成这样:

year % 100 == 0 ?
├─ 是 → year % 400 == 0 ? 是闰年 : 不是闰年
└─ 否 → year % 4 == 0 ? 是闰年 : 不是闰年

当 year = 2000 时,先判断 2000 % 100 == 0 成立,再判断 2000 % 400 == 0 也成立,所以输出“是闰年”。

熟悉之后,可以把条件合并成一行:

boolean isLeap = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
System.out.println(isLeap ? "是闰年" : "不是闰年");

这里用到了上一节的逻辑运算符。括号不能省,因为 && 的优先级高于 ||。嵌套写法更适合初学者理解判断过程,合并写法更简洁,实际开发中可以根据可读性选择。

switch:等值判断的另一种写法

当判断的是“某个变量等于几个固定值中的哪一个”时,switch 会比一长串 else if 更清楚:

int a = 1;

switch (a) {
    case 1:
        System.out.println(1);
        break;
    case 2:
        System.out.println(2);
        break;
    case 3:
        System.out.println(3);
        break;
    default:
        System.out.println("haha");
        break;
}

当 a = 1 时,程序输出 1。

switch 有几个容易出错的规则:

  1. 支持的类型有限。 常见的是 byte、short、char、int,以及它们的包装类;此外还支持 String 和枚举。long、float、double、boolean 不能作为 switch 的选择表达式。
  2. case 后面必须是常量。 不能写 case a > 1:,也不能写运行时才能确定的值。
  3. break 用来结束当前分支。 如果漏写,程序会继续执行下一个 case 的代码,这叫“贯穿”(fall through)。
  4. default 不是必须的,但建议保留。 它负责处理所有 case 都没匹配上的情况,位置可以放在任意位置,通常放在最后。
  5. 多个 case 可以共用一段逻辑:
case 1:
case 2:
    System.out.println("1 或 2");
    break;

如果使用的是 Java 14 及以上版本,还可以写成箭头形式,不需要 break,也不会发生贯穿:

switch (a) {
    case 1 -> System.out.println(1);
    case 2 -> System.out.println(2);
    default -> System.out.println("haha");
}

初学阶段先把传统的 case/break 写熟,再了解箭头语法即可。

while:条件成立就继续

while 的语法很简单:

while (布尔表达式) {
    // 循环体
}

它的执行顺序是:先判断条件,条件为 true 就执行循环体,执行完再回来判断,直到条件为 false。

写 while 时一定要保证循环变量会变化,否则很容易写出死循环。

例子:计算 5 的阶乘

int n = 1;
int result = 1;

while (n <= 5) {
    result *= n;
    n++;
}

System.out.println(result); // 120

这里的三要素是:

  • 初值:n = 1;
  • 条件:n <= 5;
  • 更新:每轮执行 n++。

如果漏掉 n++,n 永远是 1,循环就停不下来了。

例子:计算 1! + 2! + 3! + 4! + 5!

这个例子用到了嵌套循环:

int num = 1;
int sum = 0;

while (num <= 5) {
    int tmp = 1;
    int factorResult = 1;

    // 里层循环:计算 num 的阶乘
    while (tmp <= num) {
        factorResult *= tmp;
        tmp++;
    }

    // 外层循环:把每个阶乘累加
    sum += factorResult;
    num++;
}

System.out.println("sum = " + sum); // 153

可以把两层循环的职责分开理解:

  • 外层循环负责“现在要算到几的阶乘”,并负责累加;
  • 内层循环负责“把当前这个数的阶乘算出来”。

运行结果是 153,因为:

1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
1 + 2 + 6 + 24 + 120 = 153

不过这个写法每次都从头计算阶乘,效率不高。可以复用上一次的结果:

int factorial = 1;
int sum = 0;
int i = 1;

while (i <= 5) {
    factorial *= i;   // 利用上一次的 (i - 1)! 直接得到 i!
    sum += factorial;
    i++;
}

System.out.println(sum); // 153

这样每个阶乘只算一次,思路也更简洁。后面学了 for 之后,这段代码还可以写得更紧凑。

for:把循环三要素写在一起

for 循环把“初始化、条件、更新”集中写在括号里,适合循环次数比较明确的场景:

for (表达式1; 布尔表达式2; 表达式3) {
    // 循环体
}

执行顺序是:

  1. 先执行一次 表达式1;
  2. 判断 表达式2,为 true 就执行循环体;
  3. 循环体执行完,执行 表达式3;
  4. 回到第 2 步继续判断。

例子:用 for 计算 5 的阶乘

int result = 1;

for (int i = 1; i <= 5; i++) {
    result *= i;
}

System.out.println("result = " + result); // 120

i 只在 for 循环内部有效,循环结束后就不能再访问了。这是一个很好的作用域习惯:循环变量只在需要它的地方出现。

例子:用嵌套 for 求阶乘和

int sum = 0;

for (int i = 1; i <= 5; i++) {
    int tmp = 1;

    for (int j = 1; j <= i; j++) {
        tmp *= j;
    }

    sum += tmp;
}

System.out.println("sum = " + sum); // 153

这段代码和前面的 while 版本思路一致,只是把循环变量集中到了 for 的括号里,读起来更紧凑。

while 和 for 怎么选?一个简单的判断方法是:

  • 次数明确,比如“从 1 循环到 5”,优先用 for;
  • 次数不明确,比如“一直猜,直到猜中为止”,优先用 while;
  • 两者可以互相改写,选可读性更好的那个。

break 与 continue:提前结束和跳过

break 和 continue 都用于控制循环,但作用不同:

关键字作用
break立即结束最近的一层循环或 switch
continue跳过本次循环剩余的语句,直接进入下一次循环
return结束当前方法,后面的代码都不再执行

例子:找 100 到 200 中第一个 3 的倍数

int num = 100;

while (num <= 200) {
    if (num % 3 == 0) {
        System.out.println("找到了 3 的倍数, 为:" + num);
        break;
    }
    num++;
}

输出结果是:

找到了 3 的倍数, 为:102

找到第一个满足条件的数之后,break 直接结束循环,不再继续检查后面的数。

这里有一个容易踩的坑:练习代码的注释里提到“把 break 换成 continue 就能找到 100 到 200 中所有 3 的倍数”,但在这段 while 里直接替换会出问题——continue 会跳过后面的 num++,导致 num 一直停在 102,变成死循环。

更稳妥的写法是改用 for:

for (int num = 100; num <= 200; num++) {
    if (num % 3 != 0) {
        continue;
    }
    System.out.println(num);
}

for 的 num++ 会在每次循环结束后执行,即使这一轮执行了 continue,循环变量仍然会更新,所以不会死循环。

如果一定要在 while 里使用 continue,要确保更新语句在 continue 之前执行:

while (num <= 200) {
    if (num % 3 == 0) {
        System.out.println(num);
        num++;
        continue;
    }
    num++;
}

这个细节说明:continue 本身不难,难的是它跳过了哪些语句。

输入输出:让程序能和人交互

三种常见输出方式

int msg = 0;

System.out.println(msg);        // 输出并换行
System.out.print(msg);          // 输出但不换行
System.out.printf("%d\n", 19);  // 格式化输出

printf 常用的格式占位符:

占位符含义
%d整数
%s字符串
%f浮点数
%.2f保留两位小数
%n平台相关的换行符

例如:

System.out.printf("年龄:%d,成绩:%.2f%n", 18, 95.5);

用 Scanner 读取键盘输入

import java.util.Scanner;

Scanner sc = new Scanner(System.in);

System.out.println("读入一个字符串:");
String str = sc.nextLine();
System.out.println(str);

System.out.println("读入一个整型:");
int age = sc.nextInt();
System.out.println(age);

// nextInt() 只读走整数,不读走后面的换行符
// 这里先消费掉换行,避免影响后面的输入
sc.nextLine();

System.out.println("读入一个小数:");
float f = sc.nextFloat();
System.out.println(f);

Scanner 常见方法:

  • nextLine():读取一整行,包括空格;
  • next():读取一个“单词”,遇到空白就停止;
  • nextInt()、nextFloat()、nextDouble():读取对应类型的数据。

这里最容易遇到的问题是:nextInt() 读完后,换行符还留在输入缓冲区里。如果紧接着调用 nextLine(),可能直接读到一个空字符串。练习代码里的 sc.nextLine() 就是在消费这个换行符。

另外,如果输入的类型不匹配,比如在 nextInt() 时输入了字母,会抛出 InputMismatchException。实际项目中可以先用 hasNextInt() 判断,再做读取。

小项目:猜数字

把随机数、循环、分支和输入组合起来,就是一个很经典的猜数字游戏:

import java.util.Random;
import java.util.Scanner;

Random random = new Random();
int randnum = random.nextInt(100); // 生成 [0, 100) 的随机整数

System.out.println("随机数是:" + randnum);

Scanner sc = new Scanner(System.in);

while (true) {
    System.out.println("请输入你要猜的数字:");
    int num = sc.nextInt();

    if (num < randnum) {
        System.out.println("小了");
    } else if (num == randnum) {
        System.out.println("正确");
        break;
    } else {
        System.out.println("大了");
    }
}

几个关键点:

  • Random.nextInt(100) 的范围是 左闭右开 的 [0, 100),不包含 100;
  • while (true) 表示“一直猜”,真正的出口是猜中后的 break;
  • 先判断“小了”,再判断“相等”,最后用 else 处理“大了”,保证三种情况互斥;
  • 每次循环都会重新读取一个数,所以不会卡住;
  • 练习时我把随机数打印出来方便调试,真正玩的时候把这行删掉即可。

如果想把它改得更完整,可以继续做这些改进:

  1. 把范围改成 1 到 100:random.nextInt(100) + 1;
  2. 增加一个计数器,记录猜了几次;
  3. 用 hasNextInt() 处理用户输入非数字的情况;
  4. 限制最大猜测次数,超过次数就结束游戏。

几个容易踩的坑

  1. 把 == 写成 =。 if (a = 1) 在 Java 中会编译报错,因为赋值表达式的类型是 int,不是 boolean;比较是否相等要用 ==。
  2. switch 忘记 break。 代码会继续执行下一个 case,常常导致多打印内容。
  3. 循环变量不更新。 while 里忘记 i++ 或 n++,很容易死循环。
  4. continue 跳过了更新语句。 尤其在 while 中,先确认 continue 后面的 i++ 还能不能执行到。
  5. nextInt() 后直接 nextLine()。 换行符可能被下一次读取当成空行,需要额外消费一次。
  6. 误以为 nextInt(100) 包含 100。 它的上界是开区间,想要 1 到 100 要写成 nextInt(100) + 1。
  7. switch 使用了不支持的类型。 long、float、double、boolean 都不能作为选择表达式的类型。

小结

把这篇的内容压缩成一张表:

语句适合场景关键点
if / else if / else范围判断、复杂条件条件是 boolean,一组分支最多命中一个
switch固定值的等值判断case 是常量,注意 break 和类型限制
while次数不固定的循环先判断后执行,记得更新循环变量
for次数明确的循环初始化、条件、更新写在一起
break提前结束循环或 switch只跳出最近一层
continue跳过本次循环剩余部分注意不要跳过循环变量的更新

逻辑控制看起来语句不多,但真正写起来,难点往往在边界和退出条件。建议先把文中的闰年、阶乘、找倍数和猜数字自己敲一遍,再尝试改需求,比如“输出所有偶数”“统计猜数字次数”“求 1 到 100 的和”,这样才能把语法变成自己的东西。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值