3.8.2循环语句
3.8.2.1while语句
最近这些年买彩票只能去投注站买,早些年,笔者经常是在网上买。在网上买有个功能:追号。就是假如你想一直买同一组号码,直到中大奖为止。你可以设置一个条件,比如中了头奖就不继续买了,如果没有中头奖,下一期继续买同样的号码。对于这样的功能,在程序中可以采用while循环来实现:
代码如下:
while(n<5000000) { System.out.println("下一期继续买同一组号码"); }
但是事实上,我们先要买第一期,然后才能判断是否中头奖,循环才能继续:
代码如下:
do { System.out.println("买一组号码"); } while (n < 5000000);
下面我们再用一个示例来解决我们儿子最近学奥数的一个数学问题,计算1+2+3+…+100;
int sum = 0;// 最终结果,初始为0 int add = 1;// 加数,初始为1 while (add <= 100) { sum = sum + add; add++;// 加完后,自增1 } System.out.println(sum);//最终结果是5050
通过上面实例我们知道,当while的条件为真,则执行循环语句。如果这个条件一直为真的话,程序就会进入一个死循环了。因此在实际程序编写的时候,一定要保证这个条件随着程序的运行,会在某一个时刻变为假,避免程序进入死循环。比如上面这个例子变量add会自增,所以一定大于100。
3.8.2.2for语句
对于上面这个数学问题,我们可以看出来,它的循环次数是固定的,对于这种循环问题,Java还有一种更加简洁的语句来实现,就是for循环。代码可以写成这样:
int sum = 0;// 最终结果,初始为0 for (int add = 1; add <= 100; add++) { sum += add; } System.out.println(sum);// 最终结果是5050
我们可以看到,代码的第2行,把加数add的初始化、循环条件和add的自增都放到一行了,显得更加简洁。它的通用结构如下:
for(表达式1 ; 表达式2 ; 表达式3)
- 表达式1:一般用来初始化循环迭代计数器
- 表达式2:必须是一个结果为boolean的表达式,一般用作循环条件
- 表达式3:一般用来迭代循环计数器
其实对于for循环中括号内,是分成3个部分,每个部分之间用分号(;)隔开。Java允许这3个部分放置任何表达式,并且都是可以省略不写的。示意图如下:
另外,对于在表达式1中声明的变量,它的作用域是整个for循环的循环体。对于在循环语句中定义的变量,作用域只能在循环体{}内。
有的时候,在一个for循环中,会有多个计数器,例如前面追号买彩票的例子,可以设置追号10期,但是有的时候你的账户余额不足了,彩票站不会给你垫钱追号的,代码可以写成这样:
for (int balance = 10, count = 1; balance >= 2 && count <= 10; count++, balance -= 2) { System.out.println("余额还剩" + balance + "元,购买第" + count + "期彩票"); }
balance:账户余额;count:追号期数
运行结果:
余额还剩10元,购买第1期彩票
余额还剩8元,购买第2期彩票
余额还剩6元,购买第3期彩票
余额还剩4元,购买第4期彩票
余额还剩2元,购买第5期彩票
我们可以看到:
- 表达式1可以同时定义多个同类型的变量(balance和count)
- 表达式2循环条件是余额大于等于2(够一张彩票钱)并且追号期数小于等于10(我们设置的期数)
- 表达式3可以同时对balance和count进行更新
虽然Java的语法规则对for循环的表达式1、表达式2、表达式3的限制非常少,但是笔者不建议编写晦涩难懂的语句,尽量保证代码的可读性。
3.8.2.3break
在上面这个例子中,代码其实写的就有点晦涩,不易阅读。对于余额不足的情况,其实可以认为是中止循环的一个条件。我们可以用break来中止循环,代码可以改写:
int balance = 10; for (int count = 1; count <= 10; count++) { System.out.println("余额还剩" + balance + "元,购买第" + count + "期彩票"); balance -= 2; // 当余额不足2元的时候,中止循环 if (balance < 2) { break; } }
在while中同样可以使用break,我们将上面代码改写成while版本:
int balance = 10; int count = 1; while (count <= 10) { System.out.println("余额还剩" + balance + "元,购买第" + count + "期彩票"); count++; balance -= 2; // 当余额不足2元的时候,中止循环 if (balance < 2) { break; } }
这2段代码的运行结果都是:
余额还剩10元,购买第1期彩票
余额还剩8元,购买第2期彩票
余额还剩6元,购买第3期彩票
余额还剩4元,购买第4期彩票
余额还剩2元,购买第5期彩票
break关键字,只能中止当前循环,当有多个循环嵌套使用的时候,有时候想要直接中止最外层循环,对于这种需求,在C++中是使用goto关键字来实现的。我们在学习关键字的时候,发现Java将goto作为保留字了,但是却没有使用它,而是用了另外一种方法来实现。叫做带标签的break语句。
首先我们得想一个多层嵌套的例子,正当我冥思苦想的时候,突然发我儿子床头的一张乘法口诀表:
假如我们用程序打印这张表,可以用到2层嵌套的循环语句。第一层循环打印每一行的所有算式,然后我们把打印每一行的功能也用一个循环来实现即第二层循环。代码如下:
// row是行号,一共需要打印9行 for (int row = 1; row <= 9; row++) { // column是列号,对于第row行,一共需要打印row列 for (int column = 1; column <= row; column++) { System.out.print(column + "×" + row + "=" + column * row + " "); } 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
Perfect!完美的打印出来了。假如我们想在打印到第8行第5列的时候,不想打印了,代码改成下面这样:
// row是行号,一共需要打印9行 for (int row = 1; row <= 9; row++) { // column是列号,对于第row行,一共需要打印row列 for (int column = 1; column <= row; column++) { System.out.print(column + "×" + row + "=" + column * row + " "); if (row == 8 && column == 5) { break; } }
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 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
虽然第8行第5列之后的没有打印,但是第9行又打印出来了。我们再改成带标签的break。
1 print_row: // 这是一个标签 2 for (int row = 1; row <= 9; row++) { 3 // column是列号,对于第row行,一共需要打印row列 4 for (int column = 1; column <= row; column++) { 5 System.out.print(column + "×" + row + "=" + column * row + " "); 6 if (row == 8 && column == 5) { 7 break print_row;// 中止标签print_row 8 } 9 } 10 System.out.println();// 打印完一行,需要换行 11 }
运行结果:
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
这一次结果正确。我们在第1行添加一个标签“print_row”,然后在第7行中止该标签。这样做相当于跳转到标签“print_row”标记的代码块的末尾即第11行。需要注意的是,标签后面需要紧跟一个冒号(:)。
3.8.2.4continue
在上面打印乘法口诀表的例子,假如我们不想打印第4行和第4列,想想有啥办法吗?我们可以想到,当打印到第4行的时候,直接换一行去打印第5行。当打印到第4列的时候,也跳过,然后去打印第5列。对于这种需求,我们可以用到continue语句。continue的作用就是跳过当前循环体中剩余的部分,回到当前循环的首部。代码如下:
1 for (int row = 1; row <= 9; row++) { 2 /*第4行,打印换行,然后继续打印下一行*/ 3 if (row == 4) { 4 System.out.println(); 5 continue; 6 } 7 for (int column = 1; column <= row; column++) { 8 /*第4列,打6个空格,然后继续打印下一列*/ 9 if (column == 4) { 10 System.out.print(" "); 11 continue; 12 } 13 System.out.print(column + "×" + row + "=" + column * row + " "); 14 } 15 System.out.println(); 16 }
运行结果如下:
1×1=1 1×2=2 2×2=4 1×3=3 2×3=6 3×3=9 1×5=5 2×5=10 3×5=15 5×5=25 1×6=6 2×6=12 3×6=18 5×6=30 6×6=36 1×7=7 2×7=14 3×7=21 5×7=35 6×7=42 7×7=49 1×8=8 2×8=16 3×8=24 5×8=40 6×8=48 7×8=56 8×8=64 1×9=9 2×9=18 3×9=27 5×9=45 6×9=54 7×9=63 8×9=72 9×9=81
我们看到,结果第4行和第4列都空出来了。需要注意的是,continue只是跳过当前循环体的剩余部分,如果是for循环,表达式3部分还是会执行的。
continue语句也可以带标签,作用是跳到与标签匹配的循环首部(如果是for循环,则是表达式3)。我们看代码和结果:
print_row: for (int row = 1; row <= 9; row++) { for (int column = 1; column <= row; column++) { /*第4列,则直接打印下一行*/ if (column == 4) { System.out.println(); continue print_row; } System.out.print(column + "×" + row + "=" + column * row + " "); } 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 1×5=5 2×5=10 3×5=15 1×6=6 2×6=12 3×6=18 1×7=7 2×7=14 3×7=21 1×8=8 2×8=16 3×8=24 1×9=9 2×9=18 3×9=27