Java从环境到类与对象 · SSH 实操

第二单元 · 分支、循环、数组 · 第 4 / 12 节

if 与 switch:让程序选一条路

写 if、if-else、else-if 三种分支,用 switch 对 int 做多路分支,正确使用 break/default,分清区间判断与等值判断。

约 35 分钟 · 7 个步骤 · 进度只存本机

这一节仍然按“左边网页、右边终端”的方式做:网页给你完整源码和命令,终端里真的编译、运行、看输出。四种东西不要混:$ 开头的是 shell 命令,java 代码块里是完整源文件,output 是程序的标准输出(stdout),input 是提前准备好的标准输入(stdin,只读)。

第一单元你已经会写 Hello.java、用变量保存数据、用 Scanner 读整数,也会用 >、>=、== 得到 boolean。这一节只加“选择”:条件成立走哪条路,不成立走哪条路。本节不用循环、数组和自定义方法;main 仍然是入口外壳,先照写,不展开讲方法。

本节所有命令都在同一个 SSH 窗口里执行。新开窗口时,先运行 source ~/java-practice/env.sh,再确认 java -version 和 javac -version 都是 17.x;不是 17.x 就回 setup.html,不要继续。source 可能打印一行环境提示,那是 shell 的输出,不要和程序 output 混。

SSH 服务器终端Java 源码程序交互输入输出示例(只读)复制 Java 源码不带文件名;复制命令不带 $ 提示符;输出和输入示例没有复制按钮。
这是教程网页,不是在线运行环境。Java 代码和 SSH 命令都要在你的服务器上执行;网页不会执行代码、不会连接服务器、也不会接收密码。

已完成 0 / 7 步

第 1 步:条件成立才执行一个代码块

if 的骨架是:

示例if 语法骨架
if (条件) {
    条件为 true 时执行的语句;
}

条件必须是 boolean,比如 score >= 60。即使花括号里只有一行,也建议写花括号:以后往里加第二行时,不会因为少花括号而改变执行范围。

先预测:score 是 86,下面的程序会打印几行?如果把 86 改成 52,又会打印什么?

先建目录,写好 IfDemo.java。如果同名文件已经存在,if [ -e ... ] 那行会先把它改名备份,不会覆盖你上次写的内容;$(date +%s) 是当前时间戳,保证备份名不重复。

SSH 服务器终端bash
$ source ~/java-practice/env.sh
$ java -version
$ javac -version
$ mkdir -p ~/java-practice/lesson04
$ cd ~/java-practice/lesson04
$ if [ -e IfDemo.java ]; then mv IfDemo.java "IfDemo.java.bak.$(date +%s)"; fi
$ nano IfDemo.java

nano 打开后,把下面这个完整文件粘进去。按 Ctrl+O,回车确认文件名,再按 Ctrl+X 退出。保存后回到 shell,再编译运行:

Java 源码IfDemo.java
public class IfDemo {
    public static void main(String[] args) {
        int score = 86;
        if (score >= 60) {
            System.out.println("及格");
        }
        System.out.println("检查结束");
    }
}
SSH 服务器终端bash
$ ls -l IfDemo.java
$ javac -encoding UTF-8 IfDemo.java
$ java IfDemo
输出示例(只读)output不可复制
及格
检查结束

86 >= 60 是 true,所以进入花括号打印“及格”。花括号结束后,System.out.println("检查结束"); 不在 if 里面,它总会执行。把 86 改成 52 再编译运行,52 >= 60 是 false,花括号被跳过,只剩“检查结束”。

提示 先看条件,再看花括号范围

读一段分支代码时,先找到 if (条件),算出它是 true 还是 false;再只看对应的花括号。不要从第一行开始逐行猜。

参考答案 先预测:改成 52 会怎样

只会输出:

输出示例(只读)output不可复制
检查结束

因为 52 >= 60 为 false,if 的花括号没有执行。注意“检查结束”仍然会打印,它在 if 外面。

第 2 步:if-else 处理两条互斥的路

两条路只能走一条时,用 if-else:

示例if-else 语法骨架
if (条件) {
    条件为 true 时执行;
} else {
    条件为 false 时执行;
}

else 后面不写条件,它表示“上面那个条件不成立时”。else 不能单独出现,必须紧跟在某个 if 后面。

先预测:score 是 52,程序会走哪边?如果改成 86 呢?

新建 IfElseDemo.java(还是先备份同名旧文件):

SSH 服务器终端bash
$ cd ~/java-practice/lesson04
$ if [ -e IfElseDemo.java ]; then mv IfElseDemo.java "IfElseDemo.java.bak.$(date +%s)"; fi
$ nano IfElseDemo.java
Java 源码IfElseDemo.java
public class IfElseDemo {
    public static void main(String[] args) {
        int score = 52;
        if (score >= 60) {
            System.out.println("及格");
        } else {
            System.out.println("不及格");
        }
        System.out.println("检查结束");
    }
}
SSH 服务器终端bash
$ javac -encoding UTF-8 IfElseDemo.java
$ java IfElseDemo
输出示例(只读)output不可复制
不及格
检查结束

52 >= 60 是 false,所以跳过 if 的花括号,进入 else,打印“不及格”。两条分支是互斥的:不可能既打印“及格”又打印“不及格”。把 52 改成 86,就会走 if 那边,只打印“及格”。

scorescore >= 60走哪边
52falseelse:不及格
60trueif:及格
86trueif:及格

注意 60 这个边界:>= 包含等于,所以 60 算及格;如果写成 >,60 就会掉到“不及格”。边界用哪个运算符,要先想清楚业务含义。

第 3 步:else-if 做多级区间判断

分数不止“及格/不及格”两档,而是优秀、良好、及格、不及格四档。这时用 else-if 把多个条件串起来:

示例else-if 语法骨架
if (条件1) {
    条件1为 true;
} else if (条件2) {
    条件2为 true;
} else if (条件3) {
    条件3为 true;
} else {
    前面都不成立;
}

执行规则是**从上往下,遇到第一个为 true 的条件就进入它的花括号,后面的 else-if 和 else 全部跳过**。所以顺序很重要:用 >= 判断时从高到低排,用 < 判断时从低到高排;核心是不能把宽条件放在窄条件前面。

先预测:score = 86 会打印哪一档?如果把 score >= 60 放到最前面,86 又会打印什么?

SSH 服务器终端bash
$ cd ~/java-practice/lesson04
$ if [ -e ElseIfDemo.java ]; then mv ElseIfDemo.java "ElseIfDemo.java.bak.$(date +%s)"; fi
$ nano ElseIfDemo.java
Java 源码ElseIfDemo.java
public class ElseIfDemo {
    public static void main(String[] args) {
        int score = 86;
        if (score >= 90) {
            System.out.println("优秀");
        } else if (score >= 80) {
            System.out.println("良好");
        } else if (score >= 60) {
            System.out.println("及格");
        } else {
            System.out.println("不及格");
        }
    }
}
SSH 服务器终端bash
$ javac -encoding UTF-8 ElseIfDemo.java
$ java ElseIfDemo
输出示例(只读)output不可复制
良好

86 >= 90 是 false,所以看下一个;86 >= 80 是 true,进入“良好”,后面的 86 >= 60 和 else 都不再判断。else-if 不是每个条件都算一遍,而是遇到第一个 true 就停。

如果把 score >= 60 挪到最前面,86 会先满足 86 >= 60,直接打印“及格”,后面的“良好”永远轮不到。这不是编译器的问题,是顺序写错了。

注意 区间判断不要漏掉边界

score >= 80 这一档实际上表示“80 到 89”,因为 90 及以上已经被前一个 if 截走了。写多级判断时,先画一张区间表,再写条件,别一边写一边猜。

第 4 步:用 Scanner 把分数交给分支

前几步的分数写死在代码里。现在把第一单元学过的 Scanner 接回来:程序先打印提示,再等用户输入一个整数,然后走同一套 else-if。

Scanner 不在 java.lang 里,所以文件第一行要写 import java.util.Scanner;。import 只是告诉编译器“去 java.util 里找这个类”,现在照写即可;本课程后面不会再要求你背更多包名。

新建 GradeInputDemo.java:

SSH 服务器终端bash
$ cd ~/java-practice/lesson04
$ if [ -e GradeInputDemo.java ]; then mv GradeInputDemo.java "GradeInputDemo.java.bak.$(date +%s)"; fi
$ nano GradeInputDemo.java
Java 源码GradeInputDemo.java
import java.util.Scanner;

public class GradeInputDemo {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入分数:");
        int score = sc.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("不及格");
        }
    }
}

先编译。然后用 printf 把一行输入送进程序;printf 是 shell 命令,不是 Java 语法,\n 表示换行。这样每次运行都有固定输入,方便对照。

SSH 服务器终端bash
$ javac -encoding UTF-8 GradeInputDemo.java
$ printf '86\n' | java GradeInputDemo

交互输入(示例,只读):

交互输入(只读)input不可复制
86
输出示例(只读)output不可复制
请输入分数:
良好

再换一个不及格的输入:

SSH 服务器终端bash
$ printf '42\n' | java GradeInputDemo

交互输入(示例,只读):

交互输入(只读)input不可复制
42
输出示例(只读)output不可复制
请输入分数:
不及格

提示语“请输入分数:”也是程序的标准输出,所以它会出现在 output 块里;86 只出现在 input 块里,不会被打印。程序真正读到的就是 86 这个整数,Scanner.nextInt() 会跳过空白并解析一个 int。

先预测:输入 60 和 59 分别会打印什么?再自己运行对照。

参考答案 先预测:60 与 59

60 满足 60 >= 60,打印“及格”;59 不满足任何 >= 60 的条件,落到 else,打印“不及格”。边界值最值得测,因为它们最容易暴露 > 和 >= 写错。

第 5 步:switch 对 int 做等值多路分支

switch 适合“一个整数变量,对几个固定值分别做不同的事”。它做的是等值比较,不是区间比较:

示例switch 语法骨架
switch (整数表达式) {
    case 1:
        语句;
        break;
    case 2:
        语句;
        break;
    default:
        没有匹配时执行;
        break;
}
  • case 后面必须是常量,不能写 case score > 60 这种条件。
  • break 的作用是立刻跳出整个 switch;没有 break 会继续往下执行,叫“穿透”(fallthrough)。
  • default 在所有 case 都不匹配时执行;它不一定要放最后,但初学者放最后最清楚。
  • 本节只对 int 用 switch;字符串和枚举不在本阶段。

先预测:menu = 2 会打印哪一行?menu = 9 呢?

SSH 服务器终端bash
$ cd ~/java-practice/lesson04
$ if [ -e SwitchMenuDemo.java ]; then mv SwitchMenuDemo.java "SwitchMenuDemo.java.bak.$(date +%s)"; fi
$ nano SwitchMenuDemo.java
Java 源码SwitchMenuDemo.java
public class SwitchMenuDemo {
    public static void main(String[] args) {
        int menu = 2;
        switch (menu) {
            case 1:
                System.out.println("查看成绩");
                break;
            case 2:
                System.out.println("修改成绩");
                break;
            case 3:
                System.out.println("退出");
                break;
            default:
                System.out.println("无效选项");
                break;
        }
        System.out.println("菜单处理结束");
    }
}
SSH 服务器终端bash
$ javac -encoding UTF-8 SwitchMenuDemo.java
$ java SwitchMenuDemo
输出示例(只读)output不可复制
修改成绩
菜单处理结束

menu 是 2,程序直接跳到 case 2,打印“修改成绩”,遇到 break 跳出 switch,再执行 switch 后面的“菜单处理结束”。把 menu 改成 9,所有 case 都不匹配,走 default,打印“无效选项”。

switch 不是“更高级的 if”。它只能做等值匹配;要判断“大于 80 且小于 90”这种区间,仍然用 else-if。第 7 步会把两者放在一起对比。

第 6 步:故意漏掉 break,观察穿透

break 漏写时,编译器不会报错,程序照常运行,只是会从匹配的 case 一直往下执行,直到遇到 break 或 switch 结束。这种错误比编译错误更难发现,因为输出“看起来像程序在正常工作”。

先预测:menu = 1,下面的 case 1 末尾没有 break,会打印几行?

SSH 服务器终端bash
$ cd ~/java-practice/lesson04
$ if [ -e SwitchFallthroughDemo.java ]; then mv SwitchFallthroughDemo.java "SwitchFallthroughDemo.java.bak.$(date +%s)"; fi
$ nano SwitchFallthroughDemo.java
Java 源码SwitchFallthroughDemo.java
public class SwitchFallthroughDemo {
    public static void main(String[] args) {
        int menu = 1;
        switch (menu) {
            case 1:
                System.out.println("查看成绩");
            case 2:
                System.out.println("修改成绩");
                break;
            case 3:
                System.out.println("退出");
                break;
            default:
                System.out.println("无效选项");
                break;
        }
    }
}
SSH 服务器终端bash
$ javac -encoding UTF-8 SwitchFallthroughDemo.java
$ java SwitchFallthroughDemo
输出示例(只读)output不可复制
查看成绩
修改成绩

menu 匹配 case 1,打印“查看成绩”;因为没有 break,程序继续执行 case 2 的语句,打印“修改成绩”,直到遇到 case 2 里的 break 才停。穿透不是“重新判断 menu 是不是 2”,它只是顺着代码往下走。

修法很简单:在 case 1 的打印语句后面补一个 break;,就回到第 5 步那种“只执行一个分支”的行为。也有故意利用穿透的写法,比如多个 case 共用一个处理块,但初学阶段先养成“每个 case 都写 break”的习惯。

注意 漏 break 不会报错

如果 switch 的输出多了一行,先数每个 case 后面有没有 break;。不要只看最后一个 case;穿透会从匹配点一路往下串。

第 7 步:区间用 else-if,等值用 switch

现在把两种分支放在同一个程序里,练习“选工具”:

  • 年龄分三档(未成年、成人、老年)是区间判断,用 if / else-if / else。
  • 座位号 1、2、其他是固定值匹配,用 switch。

先预测:age = 17、seat = 2,程序会打印哪两行?

SSH 服务器终端bash
$ cd ~/java-practice/lesson04
$ if [ -e BranchPickDemo.java ]; then mv BranchPickDemo.java "BranchPickDemo.java.bak.$(date +%s)"; fi
$ nano BranchPickDemo.java
Java 源码BranchPickDemo.java
public class BranchPickDemo {
    public static void main(String[] args) {
        int age = 17;
        if (age < 18) {
            System.out.println("未成年票");
        } else if (age < 60) {
            System.out.println("成人票");
        } else {
            System.out.println("老年票");
        }

        int seat = 2;
        switch (seat) {
            case 1:
                System.out.println("靠窗");
                break;
            case 2:
                System.out.println("靠过道");
                break;
            default:
                System.out.println("中间");
                break;
        }
    }
}
SSH 服务器终端bash
$ javac -encoding UTF-8 BranchPickDemo.java
$ java BranchPickDemo
输出示例(只读)output不可复制
未成年票
靠过道

年龄是连续区间,所以第一个 age < 18 成立就打印“未成年票”,后面的 else-if 不再判断。座位号是离散的固定值,switch 直接跳到 case 2,打印“靠过道”,break 跳出。

如果座位 1 和 2 都要算“靠窗”,可以写两个叠在一起的 case,共用一个处理块和 break。这属于“故意穿透”的写法,等熟练后再用;现在先保证每个 case 都有 break。

说明 switch 的 case 不能写区间

case age < 18: 不是合法的 case 标签。case 后面只能是常量。区间判断请回到 if / else-if,不要硬塞进 switch。

常见错误

  • 现象: javac 报 incompatible types: int cannot be converted to boolean,指向 if (score = 60)。原因: = 是赋值,if 需要的是 boolean 表达式。先检查: 把 = 改成 ==(等值)或 >=(区间),再确认条件两边类型能比较。
  • 现象: if 后面明明写了条件,下面的语句却总是执行,或者 else 报 'else' without 'if'。原因: 在 if (...) 后面多写了一个分号,变成空语句;或者 else 前面没有紧挨着完整的 if。先检查: 看 if (...) 右括号后有没有 ;,以及 else 是否和它的 if 在同一层花括号里。
  • 现象: switch 打印了不止一个 case 的内容。原因: 某个 case 末尾漏了 break;,发生穿透。先检查: 从匹配的 case 开始,逐个看打印语句后面有没有 break;;不要只看最后一个 case。
  • 现象: javac 报 duplicate case label 或 case label must be a constant expression。原因: 两个 case 写了同一个值,或 case 后面写了变量/条件。先检查: 列出所有 case 值,确认不重复;case 只能是字面量常量。
  • 现象: 运行 GradeInputDemo 时抛出 InputMismatchException,或者程序一直停着不动。原因: 输入的不是整数(比如带了小数点、字母),或交互运行时还没按回车。先检查: 对照 input 块,确认第一行就是一个纯整数;用 printf 固定输入时检查 \n 是否写对。
  • 现象: javac 报 illegal character ':' 或 ';'。原因: 代码里混入了中文全角冒号、分号或括号。先检查: 看报错行号,把 : 改成 :、; 改成 ;、() 改成 ();写代码时保持输入法在英文状态。

练习

先自己写、先预测,再展开答案。练习题也用 JDK 17 实际编译运行过,答案里的输出就是程序真实的标准输出。

练习 1:五级成绩

把第 3 步的四档改成五档:>= 90 优秀、>= 80 良好、>= 70 中等、>= 60 及格,其余不及格。用 score = 75 运行,应该输出“中等”。要求仍然只用 if / else-if / else,不用循环和数组。

参考答案 练习 1:五级成绩

完整文件存成 Grade5Demo.java:

Java 源码Grade5Demo.java
public class Grade5Demo {
    public static void main(String[] args) {
        int score = 75;
        if (score >= 90) {
            System.out.println("优秀");
        } else if (score >= 80) {
            System.out.println("良好");
        } else if (score >= 70) {
            System.out.println("中等");
        } else if (score >= 60) {
            System.out.println("及格");
        } else {
            System.out.println("不及格");
        }
    }
}

编译运行:

SSH 服务器终端bash
$ javac -encoding UTF-8 Grade5Demo.java
$ java Grade5Demo
输出示例(只读)output不可复制
中等

75 不满足前三个条件,满足 75 >= 70,所以进入“中等”;后面的 >= 60 不再判断。

练习 2:给菜单加一项

在 SwitchMenuDemo.java 的基础上加 case 4,打印“打印帮助”,保留 default。用 menu = 4 运行,应该输出“打印帮助”。写完先自己检查每个 case 是否有 break。

参考答案 练习 2:给菜单加一项

完整文件存成 MenuHelpDemo.java:

Java 源码MenuHelpDemo.java
public class MenuHelpDemo {
    public static void main(String[] args) {
        int menu = 4;
        switch (menu) {
            case 1:
                System.out.println("查看成绩");
                break;
            case 2:
                System.out.println("修改成绩");
                break;
            case 3:
                System.out.println("退出");
                break;
            case 4:
                System.out.println("打印帮助");
                break;
            default:
                System.out.println("无效选项");
                break;
        }
    }
}

编译运行:

SSH 服务器终端bash
$ javac -encoding UTF-8 MenuHelpDemo.java
$ java MenuHelpDemo
输出示例(只读)output不可复制
打印帮助

menu 是 4,直接匹配 case 4;break 让程序跳出 switch,不会继续走 default。

练习 3:先预测,不运行

下面的代码只是片段,不用单独存成文件;先口算 score = 90 时会打印什么,再展开答案。

示例预测片段
int score = 90;
if (score > 60) {
    System.out.println("第一档");
} else if (score > 80) {
    System.out.println("第二档");
} else {
    System.out.println("第三档");
}
参考答案 练习 3:顺序陷阱

打印“第一档”。90 > 60 已经成立,程序进入第一个分支后就不再判断后面的 else if。虽然 90 > 80 也成立,但它永远轮不到。这个例子说明:else-if 的顺序会改变结果,范围判断要从高到低或从低到高排好,不能把宽条件放在窄条件前面。

做到这里

做到这里今天可以停。你已经会:

  • 用 if 让一段代码只在条件成立时执行;
  • 用 if-else 处理两条互斥的路;
  • 用 else-if 做多级区间判断,并知道顺序会影响结果;
  • 用 switch 对 int 做等值分支,知道 break 和 default 的作用;
  • 用固定 input 配合 Scanner 测试不同分数。

下一节才学 for、while 和循环里的 break、continue,以及浅层嵌套。本节没有数组、没有自定义方法;不要在练习里提前用 for 遍历或写 static 辅助方法。

来源 来源与改编说明(非官方)

主题参考:黑马程序员 Java 入门到起飞(BV17F411T7Ao,2022-04-27)P039–P044 的顺序结构、if 三种格式、switch 及扩展主题。本站为独立原创讲解,非官方站点/教材,未获授权,不逐字复刻视频或讲义;示例、错误清单与练习为原创补充。详见 source/SOURCE.md。

说明:本站是个人学习整理,不是官方教材;不包含原课程的书本、视频或受版权保护的整本内容。原课程与商标归其权利人。