Java条件语句精通指南:从基础到高级应用

作者选择为写作按捐赠计划之一捐赠款项给自由开源基金。

引言

条件语句改变程序的流程。条件语句也被称为分支语句,因为当条件匹配时,流程会进入代码的一个分支。如果条件不满足,则会评估另一个条件(如果有的话)。该评估会继续,直到所有条件都被评估为真或假为止。例如,您可以使用以下条件语句来比较两个数字(x和y):x > y、x = y和x < y。如果x等于1,y等于2,则会进行三次评估,直到最终的x < y条件语句评估为真。

有两种条件语句。第一种类型是if以及它的扩展形式else if。这种类型被广泛使用。第二种类型是switch,更具体且限制较多。

在本教程中,你将会用Java编写条件语句,并学习每种类型的使用情况、优点和缺点。

先决条件

要跟随本教程,您需要:

  • 一个可以执行Java程序的环境,以便跟随示例。要在您的本地机器上进行设置,您将需要以下内容:

    您的机器上安装了Java(版本11或更高),并带有Java开发工具包(JDK)提供的编译器。对于Ubuntu和Debian,请遵循我们教程《如何在Ubuntu 22.04上使用Apt安装Java》中选项1的步骤。对于其他操作系统,包括Mac和Windows,请遵循Java安装的下载选项。
    为了编译和运行代码示例,本教程使用Java Shell,它是一个从命令行运行的读取-评估-打印循环(REPL)。要开始使用JShell,请查阅《JShell简介》指南。

  • 熟悉Java和面向对象编程,您可以在我们的教程《如何编写您的第一个Java程序》中找到。
  • 理解Java数据类型,这在我们的教程《理解Java中的数据类型》中有所讨论。

区分语句和块

当满足条件语句并且执行流程被引导到相应的代码分支时,该分支可以是单个语句或一块代码。由于语句和代码块的处理方式不同,因此区分它们非常重要。

一条语句是一个执行单元,通常以分号结束,只占用一行。例如,使用下面的语句打印示例:

  1. System.out.println("example");

信息

为了跟随本教程中的示例代码,通过运行jshell命令在您的本地系统上打开Java Shell工具。然后可以通过在jshell>提示符后添加并按ENTER进行复制、粘贴或编辑示例。要退出jshell,请输入/exit

此语句使用println()方法来打印一个字符串,在这个案例中为“example”。除非另有指示,Java将无条件执行此语句。

注意

通常情况下,为了使语句更易读,会将其写在一行上。然而,根据官方约定指南,应避免超过80个字符的行。如果您的语句超过80个字符,应将其拆分成多行。

此外,在某些情况下,如使用lambda表达式时,将语句分散到几行中更方便、直观。无论如何,Java对语句中的换行没有语法要求,您可以在新的一行上继续语句。作为练习,尝试重写本教程中的某些示例,并将语句分隔到多行上。

当你将语句分组以便一起执行时,你使用的是一个代码块。代码块是以左大括号({)开头,以右大括号(})结尾的零个或多个语句的集合。你可以像下面这样重新写前面的代码并将其放入一个代码块中:

  1. {
  2. System.out.println("example");
  3. }

在上面的代码块中,您在System.out.println("example");之前使用了四个空格。这四个空格被称为缩进。与Python等其他编程语言不同,在Java中缩进既不是必需的,也不会影响代码。然而,为了更好的可读性,并且按照约定,块内的代码行以四个空格的缩进开始。大多数代码编辑器都支持自动缩进,但是约定在不同的编程语言甚至编辑器之间可能会有所不同。因此,如果您的代码编辑器支持缩进,请确保在编写Java代码时将其设置为四个空格。Jshell在本教程中推荐使用,并自动将四个空格设置为缩进。

在这种情况下,将代码放在一个块内不会有任何区别,因为没有条件。因此,无论语句是否在块内,示例都将被打印出来。当你在jshell中运行这两个先前的示例时,你将在两种情况下收到相同的输出。

输出
example

以上的输出结果显示,在一个代码块内或者在代码块外面打印示例的语句是以相同的方式工作的。

在本节中,您了解到了语句、代码块以及如何创建它们。下一节中,您将向代码块中添加一个条件语句。因此,代码块内的语句将会一起并且有条件地执行。

使用单一的条件语句

最常用的条件语句是if语句。只要比较结果是布尔值(真或假),它就是通用的并满足所有条件比较的需求。if条件语句可以是最简单形式的单一条件,也可以有更复杂的结构,在后面的部分中你将会使用到。

单个if语句用于在单个条件下执行一段代码。您将会将条件执行的代码放在一个代码块中,并在开头放置if (布尔表达式)。只有当表达式及其条件为真时,代码块才会被执行。如果表达式的结果为假,则条件块的代码将被跳过。

在这个例子中,你将编写一个代码片段来比较两个数字。根据比较的结果,可能会打印一条消息。将以下行添加到jshell中:

  1. int x = 1;
  2. int y = 0;
  3. if (x > y) {
  4. System.out.println(x + " is bigger than " + y);
  5. }

在前两行中,您定义了两个变量:xy。第三行是条件语句if (x > y),它比较了x是否大于y。比较操作通常位于括号中。

块代码以第一个开括号({)开始,以闭括号(})结束。在块内,一个语句使用println()方法来打印出“x大于y”。只有当这个条件为真时,块代码才会被执行。

当你在jshell中运行此代码时,将会得到以下输出:

这是文章《如何在Java中编写条件语句》的第2部分(共5部分)。

Outputx ==> 1 y ==> 0 1 is bigger than 0

输出的前两行确认了x和y的值。第三行表示1大于0,这意味着条件语句为真:x大于y。如果你将x改为小于y,那么该消息将不会被打印。

在条件语句中,最重要的规则是评估的表达式必须返回一个布尔值。否则,在任何其他情况下,都会出现错误,因为你所比较的内容无法转换为布尔值。

为了进一步练习,请尝试使用非布尔表达式引发错误。为此,将一个不能转换为布尔值的整数变量放置在条件括号中,像这样:

  1. int x = 1;
  2. if (x) {
  3. System.out.println("Will this work?");
  4. }

在第一行,你定义了一个整数变量x,并给它赋了一个值(1)。第二行是条件if (x),这会导致程序崩溃,因为x不能转换为布尔值。当你在jshell中运行以上代码时,你将会得到以下错误:

Outputx ==> 1 | Error: | incompatible types: int cannot be converted to boolean | if (x) { | ^

第一行确认x接收到值1。接下来的几行描述了不兼容类型的错误,并解释了一个整数值(如x)无法转换为布尔值。每当你遇到这样的错误,你就需要仔细检查你的比较。

像这样的非布尔条件语句可能是条件语句中最常见的陷阱。幸运的是,现代集成开发环境(IDE)在编写时就能捕捉到这种错误。即使您没有使用IDE,错误仍然很容易发现。

在没有代码块的情况下编写条件 if 语句

Java的if条件语句的一个特点是可以以更短的方式书写,而不需要使用代码块。虽然不建议在不使用代码块的情况下使用条件语句,但你应该了解到这种书写方式是可行的。在实际应用中,如果你在学习Java考试或参加工作面试时,可能会遇到类似的令人困惑的例子,所以你应该做好准备。

举个例子,你可以不用一整块的代码,而是写一个像这样的语句。

  1. int x = 1;
  2. int y = 0;
  3. if (x > y)
  4. System.out.println(x + " is bigger than " + y);

与前面的例子不同之处在于,在条件语句 if (x > y) 之后没有代码块。相反,在条件语句 if (x > y) 之后紧接着的就是目标语句 System.out.println(x + " is bigger than " + y)

当你在jshell中运行此代码时,你将得到与先前示例相同的输出结果。

Outputx ==> 1 y ==> 0 1 is bigger than 0

x和y的值将打印在前两行。1大于0的确认消息将在第三行打印。

当条件语句后没有代码块时,只有紧接在条件语句后的一行依赖于该条件。在这种情况下,无论条件如何,在第4行之后的任何语句都会执行。例如,尝试将 x 更改为 0,使其不大于 y,并添加如下的第五行代码:

  1. int x = 0;
  2. int y = 0;
  3. if (x > y)
  4. System.out.println(x + " is bigger than " + y);
  5. System.out.println("This line is always printed.");

当你在jshell中运行上述代码时,你将会得到以下输出结果:

Outputx ==> 0 y ==> 0 This line is always printed.

此外,第5行的缩进是不必要的。它只是为了暗示第5行的执行取决于条件,而实际情况并非如此。可以省略此缩进,但这并不会使代码更清晰。

使用没有代码块的条件语句既不直观也不易于追踪,这就是为什么你应该谨慎使用简写语法的原因。为了保持代码的整洁和可读性,最好使用代码块语句,因为它们更易读,并且允许你编写多行有条件执行的代码。

嵌套的if语句是一种可以编写但通常不推荐的代码示例。在下一节中,你将探索嵌套的if语句,以更好地理解它们的价值和缺点。

嵌套的If语句

为了创建更复杂的条件结构,可以将许多if语句组合起来。要创建嵌套的if语句,可以将另一个if语句放置在父if语句内部而不是在代码块中。

这种做法通常不被推荐,因为它会使你的代码变得更难阅读和理解。然而,嵌套的if语句有时确实具有实际用途,因此熟悉它们会有所帮助。

为了理解如何实现这一点,你需要通过添加一个if语句来扩展之前的例子。你将再次将y与0进行比较以评估它们是否相等。请将以下代码添加到JShell中。

  1. int x = 1;
  2. int y = 0;
  3. if (x > y) {
  4. if (y == 0) {
  5. System.out.println("This is legal but does not look clean.");
  6. }
  7. }

前三行与之前的示例保持不变:定义了两个整型变量(x和y)并进行比较。第四行,另一个if语句比较y是否等于0。如果为真,Java将执行第五行,其中有一个带有消息“这是合法的,但不够简洁”的println()语句。

当你在JShell中运行此命令时,你将得到以下输出:

Output
x ==> 1 y ==> 0 This is legal but does not look clean.

前两行确认了x和y的值。第三行显示:“这是合法的,但看起来不干净。”该消息确认代码块已经执行,因为if条件都满足:x > y和y == 0。

你可以无限嵌套if语句。然而,每个嵌套的if语句都会增加代码的复杂性,降低其可读性。这就是为什么嵌套if语句通常不被认为是良好的编程实践。

然而,嵌套的条件结构确实具有一些实际应用。有时,为了编写更短的代码并节省时间,牺牲可读性并增加复杂性可能是值得的。因此,在考试、面试或实际生产代码中,您可能会遇到嵌套语句。

使用扩展的条件语句与Else If和Else语句

如果使用else ifelse补充语句,条件语句可以进一步扩展以处理额外的匹配标准。else语句只能在初始if语句之后使用。使用else if,你可以添加无限多个额外的条件语句,类似于第一个if。使用最后的可选else,你可以指定在没有之前的条件语句匹配时程序流应该遵循的默认路径。

通过使用else ifelse,你可以将程序流分支到比单个if更多的方向。因此,你可以构建无法以其他方式构建的复杂条件结构。在不同的方向上分支代码可以帮助你处理更复杂的业务情况。然而,生产环境中的业务需求众多,并且设计流程不一定是直接的。

使用下面的代码示例来了解else ifelse语句的概念。你将扩展之前使用的代码并添加更多的条件分支。这些新的条件将继续比较x和y。

这是文章《如何在Java中编写条件语句》的第4部分(共5部分)。

  1. int x = 1;
  2. int y = 0;
  3. if (x > y) {
  4. System.out.println(x + " is bigger than " + y);
  5. } else if (x < y) {
  6. System.out.println(y + " is bigger than " + x);
  7. } else if (x == y) {
  8. System.out.println(x + " is equal to " + y);
  9. } else {
  10. System.out.println(x + " cannot be compared to " + y);
  11. }

根据上述代码,第1行和第2行定义了两个变量xy,它们的值分别为1和0。在第3行和第4行中,使用if语句评估x是否大于y。如果评估结果为真,则打印文本“x大于y”并退出条件结构。如果评估结果为假,则程序将继续执行下一个条件。到目前为止,代码与之前的示例相同。

在第5行,在if语句的闭合括号之后,紧跟着一个else if语句。它具有类似于if语句的语法:括号内有一个布尔表达式,并且后面跟有一段代码块。该表达式评估y是否大于x,与上一个if语句相反。如果表达式为真,则将执行其后的代码块,再次退出条件结构。在这种情况下,这是第6行的语句,将打印出“y大于x”。

在第7行是另一个else if条件语句。这次,您在括号中检查x是否等于y。如果为真,则将执行第8行的语句,并打印“x等于y”。此类else if语句可以无限添加,一个接一个地评估不同的条件。

在第9行出现了最终的else语句。它始终是最后一个,且没有条件,这意味着如果之前没有匹配的条件语句,其分支总是会执行。换句话说,在没有满足ifelse if条件的情况下,会执行else后面的代码块。如果这是真的,那么第10行上的代码将会执行,并打印出“x不能与y进行比较”。

在这种复杂的条件结构中,只需要第一个if语句。如果你想要进行额外的比较,可以使用零个或多个else if语句。最后,你可以加上一个可选的else语句,以匹配其他条件都不满足的情况。

当您在jshell中运行上述代码时,您将获得以下输出结果:

Output
x ==> 1 y ==> 0 1 is bigger than 0

从上面的输出中可以看出,第一行和第二行确认了xy的值分别为1和0。第三行输出“1大于0”,证实了布尔表达式(x > y)返回了true,并且在其后的代码块已经被执行。

编写ifelse ifelse语句是编写复杂条件结构的好方法,因为您可以在每个ifelse if语句中放置不同的条件。在上面的例子中,您首先比较了x > y,然后是相反条件x < y,最后是x == y。即使在只进行了少数比较的情况下,代码仍然易读且易于管理。

然而,如果你需要进行更多的比较,添加更多的else if语句会使代码难以理解甚至令人困惑。这就是为什么在可能的情况下应该使你的结构小而简单的原因。此外,在某些情况下,使用switch语句可以替代这种复杂的结构,你将在下一部分中使用它。

使用switch条件

switch语句是另一种可用于构建决策结构的条件语句。它与if语句类似,因为它可以用于有条件地控制程序流程。然而,switch语句只适用于单一变量,并将其与不同的预定义情况进行匹配。因此,您不能评估诸如 x > y 这样的布尔表达式。相反,您可以有一个int变量,并将其值与其他int变量进行匹配;当匹配成功(即case情况),程序流程将根据匹配结果进行重定向。

switch条件语句有两个重要的规则。第一个规则是比较的变量只能是特定的数据类型,比如原始类型intchar,以及引用类型StringInteger。(支持的数据类型完整列表可以在官方switch文档中找到。)因此,你不能使用布尔变量,这就是为什么switch条件语句不能完全替代if条件语句的原因。

另一个重要的规则是case语句的值必须是字面常量或常量。常量类似于变量,但在定义时前面加上关键词final。与变量相比,一旦常量被定义,其值不能改变。我们的Java系列教程将在未来详细介绍常量。

switch结构比if结构更加有限,它并不完全替代if结构。然而,switch结构具有更高的可读性,并且在可行的情况下适用于更多的比较,这是它独特的优势。

例如,设想您想将每个月份(1到12)的数字表示与对应的月份名称进行匹配。使用if-else if结构,您需要进行十二个类似的比较。但是,使用switch语句,您可以更简洁地完成这项任务,避免了重复的代码。

为了练习使用带有月份示例的switch条件语法,请将以下代码添加到JShell中。这里有一个整数变量表示一年中的月份,每个月份都有一个对应的case

  1. int monthOfYear = 1;
  2. switch (monthOfYear) {
  3. case 1:
  4. System.out.println("一月");
  5. break;
  6. case 2:
  7. System.out.println("二月");
  8. break;
  9. default:
  10. System.out.println("不是一个月份");
  11. }

第一行中,有一个值为1的变量monthOfYear。这个变量将用于switch语句。第二行是switch语句的定义。变量monthOfYear被放在括号内,表示将进行比较。之后开始了一个代码块,在其中描述了不同的case条件。

在第3行出现了第一个值为1的情况。第一个比较将是月份是否等于1。一个冒号(:)总是跟随比较的值。在第4行出现了println()方法,用于打印出如果匹配到了1,应该打印出“一月”作为月份的名称。这一行可以是零个或多个语句,也可以是零行或多行——即可以跳过。

在第一个case之后,第5行有一个break语句,它在条件满足时使得switch条件结构被退出。因此,一旦break被执行,就不会再评估其他case条件。相反,程序流会继续在switch条件的闭括号之后继续进行,这在本例中是在第11行。请注意,break是可选的,您可能会忘记包含它。在这种情况下,switch将继续评估剩余的case,而不是在匹配时跳出条件结构。这可能会导致意外的输出和难以调试的问题。

下一个case条件位于第6、7和8行,将匹配monthOfYear值是否为2。这三行与前三行具有相同的语法,但在第7行有一个新的字符串“二月”。为了简洁起见,其余十个月份的case条件省略了。否则,它们也会按照这个结构,每个命名月份都有一个新的字符串。

在第9行是一个default语句。它没有条件,因此总是匹配的。它是可选的,并且用于在没有之前列出的情况匹配时定义程序流的默认路径。它类似于if条件语句中的else语句。

在第11行,有一个闭括号用于终止条件语句的代码块。

要执行上述代码,请将其粘贴到JShell中。您将收到以下输出:

输出
monthOfYear ==> 1 一月

第一行确认了monthOfYear的值是1。第二行打印出了“一月”,这意味着值为1的条件已经匹配:monthOfYear是1,触发了下一行的语句去打印“一月”。

作为一项练习,尝试改变monthOfYear的值,增加更多的条件语句,甚至删除break语句,以理解结果将如何改变。

结论

在本教程中,您使用了条件语句来控制程序的执行流程。您学会了什么时候适合使用ifswitch语句,并编写了一些相关的代码示例。您还学会了编写干净代码的最佳实践,以及如何避免与条件语句相关的常见陷阱。

想要更多了解Java,请查看我们的Java编程指南系列。

bannerAds