十、Java 的流程控制语句(for 循环)

1 for 循环

for 语句是应用最广泛、功能最强的一种循环语句。大部分情况下,for 循环可以代替 while 循环、do while 循环。

for 语句是一种在程序执行前就要先判断条件表达式是否为真的循环语句。假如条件表达式的结果为假,那么它的循环语句根本不会执行。for 语句通常使用在知道循环次数的循环中。

for 语句语法格式如下所示。

for(表达式1;条件表达式2;表达式3) {
    语句块;
}

for 循环中 3 个条件表达式的含义如表所示。

表达式

形式

功能

举例

条件表达式 1

赋值语句

循环结构的初始部分,为循环变量赋初值

int i=1

条件表达式 2

条件语句

循环结构的循环条件

i>40

条件表达式 3

迭代语句,通常使用++-- 运算符

循环结构的迭代部分,通常用来修改循环变量的值

i++

for 关键字后面括号中的 3 个表达式必须用 “;” 隔开。for 循环中的这 3 部分以及大括号中的循环体, 4 个组成部分完美地结合在一起,简单明了。

for 循环语句执行的过程为:首先执行 表达式 1 进行初始化,然后判断 条件表达式 2 的值是否为 true,如果为 true,则执行循环体语句块;否则直接退出循环。最后执行 表达式 3,改变循环变量的值,至此完成一次循环。接下来进行下一次循环,直到 条件表达式 2 的值为 false,才结束循环,其运行流程如图所示。

for循环执行流程图.jpg例:计算 5 的阶乘,使用 for 循环的实现代码如下:

public static void main(String[] args) {
    int result = 1;
    for (int number = 1; number <= 5; number++) {
        result *= number;
    }
    System.out.print("5 的阶乘结果是:" + result);   // 输出"5的阶乘结果是:120"
}

上述语句的含义可以理解为,将 number 变量的值从 1 开始,每次递增 1,直到大于 5 时终止循环。在循环过程中,将 number 的值与当前 result 的值进行相乘。

2 带空语句的 for 循环

for 语句中初始化、循环条件以及迭代部分都可以为空语句(但分号不能省略),三者均为空的时候,相当于一个死循环。下面对这些情况依次进行介绍。

2.1 表达式 1 为空

for 语句中 表达式 1 的作用可以在程序的其他位置给出,所以当 表达式 1 为空语句时,for 语句后面括号内其他表达式执行的顺序不变。

例如,使用 for 语句的这种形式计算 1~100 所有奇数的和。

public static void main(String[] args) {
    int result = 0;
    int number = 1; // 相当于for语句的第1个表达式
    for (; number < 101; number++) {
        if (number % 2 != 0) // 如果不能整除2,说明是奇数,则进行累加
            result += number;
    }
    System.out.print("100 以内所有奇数和为:" + result);
}

输出:100 以内所有奇数和为:2500

2.2 条件表达式 2 为空

for 语句中 条件表达式 2 为空时,将没有循环的终止条件。此时 for 语句会认为 条件表达式 2 的值总是为 true ,循环无限制执行下去。因此,为了使循环达到某种条件时退出,需要在语句块中进行逻辑判断,并使用 break 语句来跳出循环,否则将产生死循环。

同样是计算 1~100 所有奇数的和,使用这种方式的代码如下。

public static void main(String[] args) {
    int result = 0;
    for (int number = 1;; number++) {
        if (number > 100)
            break; // 相当于for语句的表达式2,满足时就退出for循环
        if (number % 2 != 0) // 如果不能整除2,说明是奇数,则进行累加
            result += number;
    }
    System.out.print("100 以内所有奇数和为:" + result);
}

2.3 表达式 3 为空

for 语言中 表达式 3 为空时,也就没有设置控制变量的表达式,即每次循环之后无法改变变量的值,此时也无法保证循环正常结束。所以,一般来说,这个表达式并没有消失,而是出现在了循环体中。

同样是计算1~100 所有奇数的和,使用这种方式的代码如下:

public static void main(String[] args) {
    int result = 0;
    for (int number = 1; number < 101;) {
        if (number % 2 != 0) // 如果不能整除2,说明是奇数,则进行累加
            result += number;
        number++; // 相当于for语句的条件表达式3,每次递增1
    }
    System.out.print("100 以内所有奇数和为:" + result);
}

2.4 3 个表达式都为空

for 循环语句中,无论缺少哪部分条件表达式,都可以在程序的其他位置补充,从而保持 for 循环语句的完整性,使循环正常进行。

for 语句中循环体全为空时,即没有循环初值,不判断循环条件,循环变量不增值,此时无条件执行循环体,如果函数体内没有循环结束条件,就会形成无限循环,或者叫死循环。

例如,计算 1~100 所有奇数的和,使用这种方式的代码如下:

public static void main(String[] args) {
    int result = 0;
    int number = 1; // 相当于for语句的条件表达式1
    for (;;) {
        if (number > 100)
            break; // 相当于for语句的条件表达式2
        if (number % 2 != 0) // 如果不能整除2,说明是奇数,则进行累加
            result += number;
        number++; // 相当于for语句的条件表达式3
    }
    System.out.print("100 以内所有奇数和为: " + result);
}

3 for 循环嵌套

就像 if 可以嵌套使用,Java 允许循环嵌套。如果把一个循环放在另一个循环体内,那么就可以形成嵌套循环。

嵌套循环既可以是 for 循环嵌套 while 循环,也可以是 while 循环嵌套 do-while 循环 …… 即各种类型的循环都可以作为外层循环,也可以作为内层循环从使用频率上讲,for 循环嵌套 for 循环的出现频率远高于其他,我们以此为例讲解。

当程序遇到嵌套循环时,如果外层循环的循环条件允许,则开始执行外层循环的循环体,而内层循环将被外层循环的循环体来执行——只是内层循环需要反复执行自己的循环体而已。

当内层循环执行结束,且外层循环的循环体执行结束时,则再次计算外层循环的循环条件,决定是否再次开始执行外层循环的循环体。

根据上面分析,假设外层循环的循环次数为 n 次,内层循环的循环次数为 m 次,那么内层循环的循环体实际上需要执行 n×m 次。嵌套循环的执行流程如图所示。

嵌套循环的执行流程.jpg嵌套循环就是把内层循环当成外层循环的循环体。当只有内层循环的循环条件为 false 时,才会完全跳出内层循环,才可以结束外层循环的当次循环,开始下一次循环。下面是一个使用嵌套循环输出九九乘法表实例。

public static void main(String[] args) {
    System.out.println("乘法口诀表:");
    // 外层循环
    for (int i = 1; i <= 9; i++) {
        // 内层循环
        for (int j = 1; j <= i; j++) {
            System.out.print(j + "*" + i + "=" + j * i + "\t");
        }
        System.out.println();
    }
}

输出:

乘法口诀表:

1*1=1

1*2=2 2*2=4

1*3=3 2*3=6 3*3=9

1*4=4 2*4=8 3*4=12 4*4=16

1*5=5 2*5=10 3*5=15 4*5=20 5*5=25

1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36

1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49

1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64

1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81

从上面运行结果可以看出,进入嵌套循环时,循环变量 i 开始为 1,这时即进入了外层循环。进入外层循环后,内层循环把 i 当成一个普通变量,其值为 0。在外层循环的当次循环里,内层循环就是一 个普通循环。

实际上,嵌套循环不仅可以是两层嵌套,而且可以是三层嵌套、四层嵌套 …… 不论循环如何嵌套, 总可以把内层循环当成外层循环的循环体来对待,区别只是这个循环体里包含了需要反复执行的代码。

4 foreach 循环语句

foreach 循环语句是 Java 1.5 的新特征之一,在遍历数组、集合方面foreach 为开发者提供了极大的方便。foreach 循环语句是 for 语句的特殊简化版本,主要用于执行遍历功能的循环。

foreach 循环语句的语法格式如下:

for(类型 变量名:集合) {
    语句块;
}

其中,“类型” 为集合中的元素的类型,“变量名” 表示集合中的每一个元素,“集合” 是被遍历的集合对象或数组。每执行一次循环语句,循环变量就读取集合中的一个元素,其执行流程如图所示。

foreach 循环语句的执行流程图.jpg例 1

假设有一个数组,采用 for 语句遍历数组的方式如下:

// 声明并初始化数组
int[] numbers = { 43, 32, 53, 54, 75, 7, 10 };
System.out.println("----for----");
// for语句
for (int i = 0; i < numbers.length; i++) {
    System.out.println("Count is:" + numbers[i]);
}

numbers.length 是获得数组的长度,length 是数组的属性,numbers[i] 是通过数组下标访问数组元素。

那么采用 for-each 循环语句遍历数组的方式如下:

// 声明并初始化int数组
int[] numbers = { 43, 32, 53, 54, 75, 7, 10 };
System.out.println("----for each----");
// for-each语句
for (int item : numbers) {
    System.out.println("Count is:" + item);
}

从示例中可以发现,item 不是循环变量,它保存了集合中的元素,for-each 语句将集合中的元素一一取出来,并保存到 item 中,这个过程中不需要使用循环变量,通过数组下标访问数组中的元素。可见 for-each 语句在遍历集合的时候要简单方便得多。

从上面程序可以看出,使用 foreach 循环遍历数组元素时无须获得数组长度,也无须根据索引来访问数组元素。

foreach 循环和普通循环不同的是,它无须循环条件,无须循环迭代语句,这些部分都由系统来完成,foreach 循环自动迭代数组的每个元素,当每个元素都被迭代一次后,foreach 循环自动结束。

当使用 foreach 循环来迭代输出数组元素或集合元素时,通常不要对循环变量进行赋值,虽然这种赋值在语法上是允许的,但没有太大的实际意义,而且极容易引起错误。例如下面程序。

例 2

String[] urls = { "http://c.biancheng.net/java", "http://c.biancheng.net/c", "http://c.biancheng.net/golang/" };
// 使用foreach循环来遍历数组元素,其中 book 将会自动迭代每个数组元素
for (String url : urls) {
    url = "https://c.biancheng.net";
    System.out.println(url);
}
System.out.println(urls[0]);

输出:

https://c.biancheng.net

https://c.biancheng.net

https://c.biancheng.net

https://c.biancheng.net/java

从上面运行结果来看,由于在 foreach 循环中对数组元素进行赋值,结果导致不能正确遍历数组元素,不能正确地取出每个数组元素的值。而且当再次访问第一个数组元素时,发现数组元素的值依然没有改变。

不难看出,当使用 foreach 来迭代访问数组元素时,foreach 中的循环变量相当于一个临时变量,系统会把数组元素依次赋给这个临时变量,而这个临时变量并不是数组元素,它只是保存了数组元素的值。因此,如果希望改变数组元素的值,则不能使用这种 foreach 循环。