控制流语句

If-Then 语句

if-then 语句是所有控制流语句中最基础的一种。它指示程序仅在特定测试条件为真时执行某段代码。例如,Bicycle 类可规定刹车系统仅在自行车处于运动状态时才能降低车速。applyBrakes() 方法的一种可能实现如下:

void applyBrakes() {
    // the "if" clause: bicycle must be moving
    if (isMoving) {
        // the "the" clause: decrease current speed 
        currentSpeed--;
    }
}

如果此测试结果为 false(即自行车未处于运动状态),控制流将跳转至 if-then 语句的末尾。
此外,当”then”子句仅包含一条语句时,开括号和闭括号可省略:

void applyBrakes() {
    // same as above, but without braces
    if (isMoving)
        currentSpeed--;
}

何时省略大括号取决于个人偏好。省略大括号会使代码更脆弱。若后续在”then”子句中添加第二条语句,常见错误便是忘记添加新需要的大括号。编译器无法检测此类错误,最终只会得到错误结果。

If-Then-Else 语句

if-then-else 语句在”if”子句计算为 false 时提供次要执行路径。您可以在 applyBrakes() 方法中使用 if-then-else 语句,当自行车静止时仍触发刹车操作时执行特定动作。此处该动作仅需输出错误提示,说明自行车已处于静止状态。

void applyBrakes() {
    if (isMoving) {
        currentSpeed--;
    } else {
        System.err.println("The bicycle has already stopped.");
    }
}

以下程序 IfElseDemo 根据考试分数的数值分配等级:90%及以上为 A,80%及以上为 B,依此类推。

public class IfElseDemo {
    public static void main(String[] args) {
        int testscore = 76;
        char grade;

        if (testscore >= 90) {
            grade = 'A';
        } else if (testscore >= 80) {
            grade = 'B';
        } else if (testscore >= 70) {
            grade = 'C';
        } else if (testscore >= 60) {
            grade = 'D';
        } else {
            grade = 'F';
        }

        IO.println("Grade = " + grade);
    }
}

输出为

Grade = C

您可能注意到,testscore 的值可以同时满足复合语句中的多个表达式:76 >= 7076 >= 60。然而,一旦某个条件成立,相应的语句 (grade = ‘C’;) 就会被执行,其余条件将不再进行计算。

While 和 Do-While 语句

while 语句在特定条件为 true 时持续执行一组语句。其语法可表示为:

while(condition){
    statement(s);
}

while 语句会评估表达式,该表达式必须返回布尔值。若表达式评估结果为真,则 while 语句执行 while 代码块中的语句。while 语句会持续测试表达式并执行其代码块,直至表达式评估结果为假。使用while 语句打印1至10的数值可通过以下 WhileDemo 程序实现:

public class WhileDemo {
    public static void main(String[] args) {
        int count = 1;
        while (count < 11) {
            IO.println("Count is: " + count);
            count++;
        }
    }
}

你可以使用while语句实现无限循环,如下所示:

while(true) {
    // your code goes here
}

Java 编程语言还提供了 do-while 语句,其表达形式如下:

do {
    statement(s);
} while (expression);

do-whilewhile 的区别在于,do-while 在循环末尾而非开头评估其表达式。因此,do 代码块内的语句至少会执行一次,如下面的 DoWhileDemo 程序所示:

public class DoWhileDemo {
    public static void main(String[] args) {
        int count = 1;
        do {
            IO.println("Count is: " + count);
            count++;
        } while (count < 11);
    }
}

For 语句

for 语句提供了一种紧凑的方式来遍历一组值。程序员常将其称为”for循环”,因为它会反复循环直至某个特定条件满足。for 语句的一般形式可表示如下:

for(initialization; termination; iteration){
    statement(s);
}

使用此版本的 for 语句时,请注意:

  • 初始化表达式用于初始化循环;它在循环开始时执行一次。
  • 当终止表达式评估为 false 时,循环终止。
  • 增量表达式在每次循环迭代后被调用;该表达式对值进行递增或递减操作是完全可行的。

以下程序 ForDemo 使用 for 语句的一般形式,将数字 1 到 10 打印到标准输出:

public class ForDemo {
    public static void main(String[] args) {
        for (int i = 1; i < 11; i++) {
            IO.println("Count is : " + i);
        }
    }
}

这个程序的输出是:

Count is : 1
Count is : 2
Count is : 3
Count is : 4
Count is : 5
Count is : 6
Count is : 7
Count is : 8
Count is : 9
Count is : 10

请注意代码如何在初始化表达式中声明变量。该变量的作用域从声明处延伸至 for 语句所控制的代码块末尾,因此也可用于终止条件和递增表达式中。若控制 for 语句的变量在循环外部无需使用,最佳做法是在初始化表达式中声明该变量。变量名 i、jk 常用于控制 for 循环;在初始化表达式中声明它们可限制其作用域并减少错误。

for 循环的三个表达式都是可选的;无限循环可按以下方式创建:

// infinite loop
for(; ;){
    // your code goes here
}

for 语句还提供另一种形式,专用于遍历集合和数组。这种形式有时被称为增强型 for 语句,可使循环代码更紧凑且易于阅读。以下是一个存储 1 到 10 的数组作为示例:

int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

以下程序 EnhancedForDemo 使用增强的 for 循环遍历数组:

public class EnhancedForDemo {
    public static void main(String[] args) {
        int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        for (int item : numbers) {
            IO.println("Count is: " + item);
        }
    }
}

在此示例中,变量 item 存储了 numbers 数组的当前值。该程序的输出结果与之前相同:

Count is: 1
Count is: 2
Count is: 3
Count is: 4
Count is: 5
Count is: 6
Count is: 7
Count is: 8
Count is: 9
Count is: 10

我们建议在可能的情况下,优先使用这种形式的 for 语句,而非通用形式。

Break 语句

break 语句有两种形式:带标签的和不带标签的。在前文关于 switch 语句的讨论中,你已经见过不带标签的形式。你也可以使用不带标签的 break 语句来终止 for、whiledo-while 循环,如下面的 BreakDemo 程序所示:

public class BreakDemo {
    public static void main(String[] args) {
        int[] arrayOfInts
                = {32, 87, 3, 589,
                    12, 1076, 2000,
                    8, 622, 127};
        int searchfor = 12;

        int i;
        boolean foundIt = false;

        for (i = 0; i < arrayOfInts.length; i++) {
            if (arrayOfInts[i] == searchfor) {
                foundIt = true;
                break;
            }
        }
        if (foundIt) {
            IO.println("Found " + searchfor + " at index " + i);
        } else {
            IO.println(searchfor + " not in array");
        }
    }
}

该程序在数组中搜索数字12。当找到该值时,break 语句将终止 for 循环。控制流随后转移到for循环后的语句。该程序的输出为:

Found 12 at index 4

未标记的 break 语句会终止最内层的 switch、for、whiledo-while 语句,而带标签的 break则会终止外层语句。以下程序 BreakWithLabelDemo 与前例类似,但使用嵌套 for 循环在二维数组中搜索值。当找到目标值时,带标签的break 会终止外层 for 循环(标记为”search”):

public class BreakWithLabelDemo {
    public static void main(String[] args) {
        int[][] arrayOfInts = {
            {32, 87, 3, 589},
            {12, 1076, 2000, 8},
            {622, 127, 77, 955}
        };

        int searchfor = 12;

        int i;
        int j = 0;
        boolean foundIt = false;

        search:
        for (i = 0; i < arrayOfInts.length; i++) {
            for (j = 0; j < arrayOfInts[i].length; j++) {
                if (arrayOfInts[i][j] == searchfor) {
                    foundIt = true;
                    break search;
                }
            }
        }

        if (foundIt) {
            IO.println("Found " + searchfor + " at index " + i + ", " + j);
        } else {
            IO.println(searchfor + " not in the array");
        }
    }
}

该程序的输出为:

Found 12 at index 1, 0

break 语句终止标记语句的执行;它不会将控制流转移到标记处。控制流将转移到标记语句(终止语句)之后紧接的语句。Java 中的标签(Label 必须紧接在它所引用的语句或块(Block)之前,并且它的作用范围仅限于它所标记的语句或块本身

Continue 语句

continue 语句用于跳过 for、whiledo-while 循环的当前迭代。无标签形式会跳至最内层循环体的末尾,并评估控制循环的布尔表达式。以下程序 ContinueDemo 遍历字符串,统计字母 p 的出现次数。若当前字符非 p,则 continue 语句跳过循环剩余部分并处理下一个字符;若为 p,则程序递增字母计数。

public class ContinueDemo {
    public static void main(String[] args) {
        String searchMe = "peter piper picked a " + "peck of pickled peppers";
        int max = searchMe.length();
        int numPs = 0;

        for (int i = 0; i < max; i++) {
            if (searchMe.charAt(i) != 'p') {
                continue;
            }
            numPs++;
        }
        IO.println("Found " + numPs + " p's in the string.");
    }
}

程序输出为:

Found 9 p's in the string.

要更清楚地看到这种效果,请尝试删除 continue 语句并重新编译。再次运行程序时,计数结果将出错,显示找到 35 个字母 p 而不是 9 个。
带标签的 continue 语句会跳过当前外层循环中带有该标签的迭代。
以下示例程序 ContinueWithLabelDemo 使用嵌套循环在字符串中搜索子字符串。需要两个嵌套循环:一个遍历子字符串,另一个遍历待搜索的字符串。该程序通过带标签的 continue 测试跳过外层循环的迭代。

public class ContinueWithLabelDemo {
    public static void main(String[] args) {
        String searchMe = "Look for a substring in this string.";
        String subString = "str";
        boolean foundIt = false;

        int max = searchMe.length() - subString.length();

        test:
        for (int i = 0; i <= max; i++) {
            int n = subString.length();
            int j = i;
            int k = 0;
            while (n-- != 0) {
                if (searchMe.charAt(j++) != subString.charAt(k++)) {
                    continue test;
                }
            }
            foundIt = true;
            break test;
        }
        IO.println(foundIt ? "Found it" : "Didn't find it");
    }
}

该程序的输出为:

Found it

Return 语句

下一个分支语句是 return 语句。return 语句将退出当前方法,控制流返回至方法被调用的位置。return 语句有两种形式:一种返回值,另一种不返回值。要返回值,只需在 return 关键字后放置该值(或计算该值的表达式)。

return ++count;

返回值的数据类型必须与方法声明的返回值类型一致。当方法声明为 void 时,请使用不返回值的 return 形式。

return;

类与对象章节将涵盖编写方法所需掌握的所有知识。

Yield 语句

最后一个分支语句是 yield 语句。yield 语句将退出当前所在的 switch 表达式。yield 语句之后必须跟随一个能产生值的表达式,该表达式不能为 void 类型。此表达式的值即为包含它的 switch 表达式所产生的值。
以下是一个 yield 语句的示例。写这个代码的人正在计算还要上几天班。

public class YieldDemo {

    enum Day {
        MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
    }

    public int calculate(Day d) {
        return switch (d) {
            case SATURDAY, SUNDAY -> 0;
            default -> {
                int remindingWorkDays = 5 - d.ordinal();
                yield remindingWorkDays;
            }
        };
    }
}